Opened 9 years ago

Closed 6 years ago

#9518 closed Bugs (fixed)

string_ref::rfind return value offset

Reported by: Peter A. Bigot <pab@…> Owned by: No-Maintainer
Milestone: To Be Determined Component: utility
Version: Boost 1.55.0 Severity: Problem
Keywords: Cc:

Description

The value returned from basic_string_ref::rfind() is calculated based on the where the end of the target string was found, but is not adjusted for the length of that string.

E.g, given

    const char pstr[] = "abcdabc\0abcd";
    string_view sv(pstr, sizeof(pstr)-1);
    const char * pabc = "abc";
    string_view svabc(pabc);

    string_ref::size_type rv = sv.rfind(svabc);

rv will have the value 10 (referencing the last 'c') instead of 8 (referencing the 'a' that starts the match).

Change History (11)

comment:1 by bimaster@…, 7 years ago

proposed fix (not fixed yet in 1.59.beta):

size_type rfind(basic_string_ref x, size_t pos = npos) const {
    const_reverse_iterator s = this->crbegin();
    const_reverse_iterator e = this->crend();
    if(pos < size()) s += pos;
    const_reverse_iterator iter = std::search(s, e, x.crbegin(), x.crend(), traits::eq);
    // the only case where substring is searched, reverse_distance not applicable
    return iter == e ? npos : len_ - std::distance(this->crbegin(), iter) - x.size();
}

in reply to:  1 ; comment:2 by bibmaster@…, 7 years ago

Replying to bimaster@…:

proposed fix (not fixed yet in 1.59.beta):

size_type rfind(basic_string_ref x, size_t pos = npos) const {
    const_reverse_iterator s = this->crbegin();
    const_reverse_iterator e = this->crend();
    if(pos < size()) s += pos;
    const_reverse_iterator iter = std::search(s, e, x.crbegin(), x.crend(), traits::eq);
    // the only case where substring is searched, reverse_distance not applicable
    return iter == e ? npos : len_ - std::distance(this->crbegin(), iter) - x.size();
}

ooops, pos in std::string denotes offset from start, so

if(pos < size()) s += (size() - pos);

in reply to:  2 ; comment:3 by bibmaster@…, 7 years ago

Replying to bibmaster@…:

ooops, pos in std::string denotes offset from start, so

if(pos < size()) s += (size() - pos);

hmmm, but pos shall be included in search range:

if(pos < size()) s += (size() - pos + 1);

in reply to:  3 ; comment:4 by bibmaster@…, 7 years ago

if(pos < size()) s += (size() - pos + 1);

error in sign:

if(pos < size()) s += (size() - pos - 1);

in reply to:  4 ; comment:5 by bibmaster@…, 7 years ago

Replying to bibmaster@…: well, error again: pos is the maximum position where found substring may start:

if(pos < size() && pos + x.size() < size())
    s += (size() - pos - x.size() - 1);

in reply to:  5 comment:6 by bibmaster@…, 7 years ago

Replying to bibmaster@…:

Replying to bibmaster@…: well, error again:

and again...

if(pos < size() && (pos + x.size()) < size())
    s += (size() - (pos + x.size()));

comment:7 by Peter A. Bigot <pab@…>, 7 years ago

Would you please wait until you have a solution that you're absolutely sure works before posting candidate fixes? Though I'm sure the Boost folks would appreciate a patch (which probably should be submittted through the mailing list or a pull request), I'm getting email on each one of these and it's a bit annoying. Thanks.

comment:8 by Alex L. <alxlaus@…>, 7 years ago

In my opinion the proposed fix goes in the wrong direction. The core of the problem lies in the reverse_distance helper, which ought to incorporate a more general length of the string as an offset (currently it is hard-coded to "1", which nicely fits the other usages, but not that in rfind).

I created a PR along with a test case here: https://github.com/boostorg/utility/pull/21

comment:9 by denis.martinez@…, 6 years ago

My dev team has hit this bug a few times. It would be really great to have the proposed fix applied, sadly there doesn't seem to be any activity for a bit of time.

This bug remains present in the 1.61.0 release of Boost.

boost::string_ref("abcdefg").rfind("cde") -> 4
std::string("abcdefg").rfind("cde") -> 2

comment:10 by Marshall Clow, 6 years ago

Commit 39577f86d1b3a260afc1e7f7caec1ee8f12594bb should fix this.

comment:11 by Marshall Clow, 6 years ago

Resolution: fixed
Status: newclosed

Merged to master.

Note: See TracTickets for help on using tickets.