When sText and sMapped have different lengths (e.g. sText="a"; sMapped="aa"), the two while loop may have out of bounds read, second loop may overflow.
Following is the patch I currently test (needs better function/variable names), it's produced with following steps.
Simplify the code by adding 1 for lastDifferenceText and lastDifferenceMapped, this changed the code to following:
size_tfirstDifference=0;while(sMapped[firstDifference]==sText[firstDifference]){firstDifference++;}size_tlastDifferenceText=sText.size();size_tlastDifferenceMapped=sMapped.size();while(sMapped[lastDifferenceMapped-1]==sText[lastDifferenceText-1]){lastDifferenceText--;lastDifferenceMapped--;}constsize_tendDifferenceText=sText.size()-lastDifferenceText;constSci::PositionlengthChange=lastDifferenceMapped-firstDifference;// pdoc->DeleteChars() and pdoc->InsertString()
Fix out of bounds in the while loop, using code similar to Document::TrimReplacement():
RangeFindDifference(std::string_viewview,std::string_viewtext)noexcept{Rangerange;while(!view.empty()&&!text.empty()&&view.front()==text.front()){range.start++;text.remove_prefix(1);view.remove_prefix(1);}while(!view.empty()&&!text.empty()&&view.back()==text.back()){range.end++;text.remove_suffix(1);view.remove_suffix(1);}returnrange;}constRangedifference=FindDifference(sMapped,sText);constsize_tfirstDifference=difference.start;constsize_tlastDifferenceText=sText.size()-difference.end;constsize_tlastDifferenceMapped=sMapped.size()-difference.end;constsize_tendDifferenceText=sText.size()-lastDifferenceText;// pdoc->DeleteChars() and pdoc->InsertString()
Extract common expression and simplify the arithmetic, got following:
simplified the code further as following, moved two views above the if condition to avoid decouple SSO string data and length twice.
std::string_viewtext=sText;std::string_viewmapped=sMapped;if(mapped!=text){size_tfirstDifference=0;// similar to Document::TrimReplacement()while(!mapped.empty()&&!text.empty()&&mapped.front()==text.front()){firstDifference++;text.remove_prefix(1);mapped.remove_prefix(1);}while(!mapped.empty()&&!text.empty()&&mapped.back()==text.back()){text.remove_suffix(1);mapped.remove_suffix(1);}constSci::PositioninsertPos=currentNoVS.Start().Position()+firstDifference;pdoc->DeleteChars(insertPos,text.length());constSci::PositionlengthInserted=pdoc->InsertString(insertPos,mapped);// Automatic movement changes selection so reset to exactly the same as it was.constSci::PositiondiffSizes=lengthInserted-text.length();}
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I'm not sure this is possible. There has to be an input text that case maps to a different output where one is smaller but the smaller is a suffix of the larger. "a" -> "aa" (or reverse) isn't a feasible case mapping.
For input > output, there has to be some prefix of the input that is removed by the case map function. For output > input, some new text has to be inserted at the front of the input. This sort of result could occur for a normalization function that, for example, removed combining accents but I don't think it will occur for case mapping.
There could be some point in hardening against potential problems, either bugs or limitations in any platform calls, with the CaseMapString method.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
That's not possible in sensible implementation, but new code is more simple and readable.
I patched ScintillaWin::CaseMapString() to do custom text transliteration (which is the easiest way to handle multiple selections, much easy than iterate and manipulate selections in application). I found out of bounds read when doing C escape/unescape (e.g. \\ => \\\\).
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Following is the patch I currently test (needs better function/variable names), it's produced with following steps.
Simplify the code by adding 1 for
lastDifferenceTextandlastDifferenceMapped, this changed the code to following:Fix out of bounds in the while loop, using code similar to
Document::TrimReplacement():Extract common expression and simplify the arithmetic, got following:
Not sure whether worth it,
pdoc->InsertString()can be simplified by pass first view by reference (as inTrimReplacement()).or following code:
or following:
simplified the code further as following, moved two views above the if condition to avoid decouple SSO string data and length twice.
I'm not sure this is possible. There has to be an input text that case maps to a different output where one is smaller but the smaller is a suffix of the larger. "a" -> "aa" (or reverse) isn't a feasible case mapping.
For input > output, there has to be some prefix of the input that is removed by the case map function. For output > input, some new text has to be inserted at the front of the input. This sort of result could occur for a normalization function that, for example, removed combining accents but I don't think it will occur for case mapping.
There could be some point in hardening against potential problems, either bugs or limitations in any platform calls, with the
CaseMapStringmethod.That's not possible in sensible implementation, but new code is more simple and readable.
I patched
ScintillaWin::CaseMapString()to do custom text transliteration (which is the easiest way to handle multiple selections, much easy than iterate and manipulate selections in application). I found out of bounds read when doing C escape/unescape (e.g.\\=>\\\\).OK, I'll look at this after the release.
Committed with [92a455].
Related
Commit: [92a455]