Closed DeveloperPaul123 closed 1 year ago
@tcbrindle I filled this PR to see if I'm on the right track (any feedback much appreciated) and also to make it know that the work is in progress for that particular issue.
Thanks for working on this! It looks like there's a syntax error which is tripping up the non-Windows builds, but the general approach looks good to me 👍
Patch coverage: 94.59%
and project coverage change: +0.12%
:tada:
Comparison is base (
051dce9
) 97.58% compared to head (96d201f
) 97.70%. Report is 30 commits behind head on main.
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
I've noticed that with the any_of
move, split()
seems to have broken? I'm getting an exception with this code in test_split.cpp
{
auto sv = "the quick brown fox"sv;
auto split = flux::split(flux::ref(sv), ' ');
using S = decltype(split);
static_assert(flux::multipass_sequence<S>);
static_assert(flux::contiguous_sequence<flux::element_t<S>>);
static_assert(flux::multipass_sequence<S const>);
static_assert(flux::contiguous_sequence<flux::element_t<S const>>);
STATIC_CHECK(check_equal(std::move(split).map(to_string_view),
std::array{"the"sv, "quick"sv, "brown"sv, "fox"sv}));
}
Just looking for an initial round of feedback; I'll have to address the build issues.
I've noticed that with the
any_of
move,split()
seems to have broken? I'm getting an exception with this code intest_split.cpp
{ auto sv = "the quick brown fox"sv; auto split = flux::split(flux::ref(sv), ' '); using S = decltype(split); static_assert(flux::multipass_sequence<S>); static_assert(flux::contiguous_sequence<flux::element_t<S>>); static_assert(flux::multipass_sequence<S const>); static_assert(flux::contiguous_sequence<flux::element_t<S const>>); STATIC_CHECK(check_equal(std::move(split).map(to_string_view), std::array{"the"sv, "quick"sv, "brown"sv, "fox"sv})); }
This is caused by the new specialisation of find
(which split
uses) returning the wrong thing in some cases. See the review comments above (the any_of
thing is just a coincidence).
I believe I've addressed most, if not all of your comments. Unit tests are also all passing for me. Please take another look when you have a moment. Thanks!
Whoops! I missed that, thanks!
Great!
Is there a need for more unit tests to make codecov happy?
Looking at the CodeCov complaints, most of them seem to be because it doesn't understand std::is_constant_evaluated()
This is technically a runtime branch, but we can't ever test it in a way that will make CodeCov happy so we'll just ignore it.
The other complaint is that we're not testing the short-circuit case in equal
when both sequences are sized but their sizes differ... but that definitely looks like it's being tested so my guess is that the compiler has inlined the it into the test function, so as far as CodeCov is concerned the line is never executed.
So I think we're fine :)
Hmmm, there is one thing though.
I was just looking back at the Nanorange issue to double-check that the conditions are now correct, and I'd forgotten about this comment from Eric:
When using the mem functions from an algorithm used with raw pointers as iterators, be sure to check the pointers for null before calling the mem functions. IIRC, they don't appreciate being called with null.
Although flux::data()
"should" never return nullptr
, there are a couple of ways that this might happen if developers get things wrong.
Would you mind adding lines equivalent to FLUX_ASSERT(flux::data(seq) != nullptr)
before the calls to the various std::memxxx
functions, so we can try to avoid UB even in the case of buggy sequence implementations?
Sorry to add more work at the last minute, I should have thought about it earlier 😞
Done! No problem at all. But note that tests now fail. Do we need to take into account flux::empty<>
sequences?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test-libflux.exe is a Catch v2.13.7 host application.
Run with -? for options
-------------------------------------------------------------------------------
drop
-------------------------------------------------------------------------------
flux/test/test_drop.cpp(104)
...............................................................................
flux/test/test_drop.cpp(104): FAILED:
due to unexpected exception with message:
flux/include\flux/op/equal.hpp:62: Fatal error: assertion
'flux::data(seq2) != nullptr' failed
===============================================================================
test cases: 74 | 73 passed | 1 failed
assertions: 556 | 555 passed | 1 failed
Done! No problem at all. But note that tests now fail. Do we need to take into account flux::empty<> sequences?
Thanks! I'd completely forgotten about flux::empty
... so much for "nullptr should never happen"! (But it's good that we caught it)
So I guess what we really need to do is check the size of the sequence first... if that is zero then we can exit early, as there's no work to do. Otherwise, we get the data pointer and assert that it's not nullptr, and then finally call the C library function. That is, something like:
auto size = flux::usize(seq);
if (size == 0) {
return;
}
auto data = flux::data(seq);
FLUX_ASSERT(data != nullptr);
std::memset(data, val, size);
(adjusted as appropriate for each case).
This turned out to be more complicated than I thought!
Sounds good, I'll get to work on that.
How would you like to handle the different cases in compare()
? What would make the most sense if both sequences are empty? Or if only one sequence is empty?
Maybe I should just look at the unit tests 😅
How would you like to handle the different cases in
compare()
? What would make the most sense if both sequences are empty? Or if only one sequence is empty?
For compare()
, I think we get the right behaviour if we do something like:
auto const seq1_size = flux::usize(seq1);
auto const seq2_size = flux::usize(seq2);
auto min_size = std::min(seq1_size, seq2_size);
int cmp_result = 0;
if (min_size > 0) {
auto const data1 = flux::data(seq1);
FLUX_ASSERT(data1 != nullptr);
auto const data2 = flux::data(seq2);
FLUX_ASSERT(data2 != nullptr);
cmp_result = std::memcmp(data1, data2, min_size);
}
That is, we only call the C function if the number of bytes is greater than zero, also making sure that the data pointers are not nullptr.
This is getting messier and messier, sorry!
Ok I think this is ready now and all tests pass for me!
I was able to add some tests, but not sure if I covered everything you had in mind. I also address all (I think) your feedback from the latest review. Sorry for the silly oversights at times and thanks for all the feedback!
Looks like I didn't cover all the cases for compare()
. Some help with this would be appreciated.
Done!
It's done!! 🎉
Thanks so much!
Description
Adds low level optimizations to address #57
Optimizations
std::memset
withfill()
std::memcmp
withequal()
std::memcmp
withcompare()
std::memchr
withfind()
std::memmem
withsearch()