| 1294 | |
| 1295 | |
| 1296 | size_t PredictReplacementSize(ptrdiff_t aLengthDelta, int aReplacementCount, int aLimit, size_t aHaystackLength |
| 1297 | , size_t aCurrentLength, size_t aEndOffsetOfCurrMatch) |
| 1298 | // Predict how much size the remainder of a replacement operation will consume, including its actual replacements |
| 1299 | // and the parts of haystack that won't need replacement. |
| 1300 | // PARAMETERS: |
| 1301 | // - aLengthDelta: The estimated or actual difference between the length of the replacement and what it's replacing. |
| 1302 | // A negative number means the replacement is smaller, which will cause a shrinking of the result. |
| 1303 | // - aReplacementCount: The number of replacements so far, including the one the caller is about to do. |
| 1304 | // - aLimit: The *remaining* number of replacements *allowed* (not including the one the caller is about to do). |
| 1305 | // - aHaystackLength: The total length of the original haystack/subject string. |
| 1306 | // - aCurrentLength: The total length of the new/result string including the one the caller is about to do. |
| 1307 | // - aEndOffsetOfCurrMatch: The offset of the char after the last char of the current match. For example, if |
| 1308 | // the empty string is the current match and it's found at the beginning of haystack, this value would be 0. |
| 1309 | { |
| 1310 | // Since realloc() is an expensive operation, especially for huge strings, make an extra |
| 1311 | // effort to get a good estimate based on how things have been going so far. |
| 1312 | // While this should definitely improve average-case memory-utilization and usually performance |
| 1313 | // (by avoiding costly realloc's), this estimate is crude because: |
| 1314 | // 1) The length of what is being replaced can vary due to wildcards in pattern, etc. |
| 1315 | // 2) The length of what is replacing it can vary due to backreferences. Thus, the delta |
| 1316 | // of each replacement is only a guess based on that of the current replacement. |
| 1317 | // 3) For code simplicity, the number of upcoming replacements isn't yet known; thus a guess |
| 1318 | // is made based on how many there have been so far compared to percentage complete. |
| 1319 | |
| 1320 | INT_PTR total_delta; // The total increase/decrease in length from the number of predicted additional replacements. |
| 1321 | int repl_multiplier = aLengthDelta < 0 ? -1 : 1; // Negative is used to keep additional_replacements_expected conservative even when delta is negative. |
| 1322 | |
| 1323 | if (aLengthDelta == 0) // Avoid all the calculations because it will wind up being zero anyway. |
| 1324 | total_delta = 0; |
| 1325 | else |
| 1326 | { |
| 1327 | if (!aHaystackLength // aHaystackLength can be 0 if an empty haystack being replaced by something else. If so, avoid divide-by-zero in the prediction by doing something simpler. |
| 1328 | || !aEndOffsetOfCurrMatch) // i.e. don't the prediction if the current match is the empty string and it was found at the very beginning of Haystack because it would be difficult to be accurate (very rare anyway). |
| 1329 | total_delta = repl_multiplier * aLengthDelta; // Due to rarity, just allow room for one extra after the one we're about to do. |
| 1330 | else // So the above has ensured that the following won't divide by zero anywhere. |
| 1331 | { |
| 1332 | // The following doesn't take into account the original value of aStartingOffset passed in |
| 1333 | // from the caller because: |
| 1334 | // 1) It's pretty rare for it to be greater than 0. |
| 1335 | // 2) Even if it is, the prediction will just be too conservative at first, but that's |
| 1336 | // pretty harmless; and anyway each successive realloc followed by a match makes the |
| 1337 | // prediction more and more accurate in spite of aStartingOffset>0. |
| 1338 | // percent_complete must be a double because we need at least 9 digits of precision for cases where |
| 1339 | // 1 is divided by a big number like 1 GB. |
| 1340 | double percent_complete = aEndOffsetOfCurrMatch // Don't subtract 1 (verified correct). |
| 1341 | / (double)aHaystackLength; // percent_complete isn't actually a percentage, but a fraction of 1. e.g. 0.5 rather than 50. |
| 1342 | int additional_replacements_expected = percent_complete >= 1.0 ? 0 // It's often 100% complete, in which case there's hardly ever another replacement after this one (the only possibility is to replace the final empty-string in haystack with something). |
| 1343 | : (int)( |
| 1344 | (aReplacementCount / percent_complete) // This is basically "replacements per percentage point, so far". |
| 1345 | * (1 - percent_complete) // This is the percentage of haystack remaining to be scanned (e.g. 0.5 for 50%). |
| 1346 | + 1 * repl_multiplier // Add 1 or -1 to make it more conservative (i.e. go the opposite direction of ceil when repl_multiplier is negative). |
| 1347 | ); |
| 1348 | // additional_replacements_expected is defined as the replacements expected *after* the one the caller |
| 1349 | // is about to do. |
| 1350 | |
| 1351 | if (aLimit >= 0 && aLimit < additional_replacements_expected) |
| 1352 | { // A limit is currently in effect and it's less than expected replacements, so cap the expected. |
| 1353 | // This helps reduce memory utilization. |
no outgoing calls
no test coverage detected