(input, empty string, treatUnknownAsEmpty, errOnEmpty, errOnUnknown bool, f ReplacementFunc, )
| 173 | } |
| 174 | |
| 175 | func (r *Replacer) replace(input, empty string, |
| 176 | treatUnknownAsEmpty, errOnEmpty, errOnUnknown bool, |
| 177 | f ReplacementFunc, |
| 178 | ) (string, error) { |
| 179 | if !strings.Contains(input, string(phOpen)) && !strings.Contains(input, string(phClose)) { |
| 180 | return input, nil |
| 181 | } |
| 182 | |
| 183 | var sb strings.Builder |
| 184 | |
| 185 | // it is reasonable to assume that the output |
| 186 | // will be approximately as long as the input |
| 187 | sb.Grow(len(input)) |
| 188 | |
| 189 | // iterate the input to find each placeholder |
| 190 | var lastWriteCursor int |
| 191 | |
| 192 | // fail fast if too many placeholders are unclosed |
| 193 | var unclosedCount int |
| 194 | |
| 195 | scan: |
| 196 | for i := 0; i < len(input); i++ { |
| 197 | // check for escaped braces |
| 198 | if i > 0 && input[i-1] == phEscape && (input[i] == phClose || input[i] == phOpen) { |
| 199 | sb.WriteString(input[lastWriteCursor : i-1]) |
| 200 | lastWriteCursor = i |
| 201 | continue |
| 202 | } |
| 203 | |
| 204 | if input[i] != phOpen { |
| 205 | continue |
| 206 | } |
| 207 | |
| 208 | // our iterator is now on an unescaped open brace (start of placeholder) |
| 209 | |
| 210 | // too many unclosed placeholders in absolutely ridiculous input can be extremely slow (issue #4170) |
| 211 | if unclosedCount > 100 { |
| 212 | return "", fmt.Errorf("too many unclosed placeholders") |
| 213 | } |
| 214 | |
| 215 | // find the end of the placeholder |
| 216 | end := strings.Index(input[i:], string(phClose)) + i |
| 217 | if end < i { |
| 218 | unclosedCount++ |
| 219 | continue |
| 220 | } |
| 221 | |
| 222 | // if necessary look for the first closing brace that is not escaped |
| 223 | for end > 0 && end < len(input)-1 && input[end-1] == phEscape { |
| 224 | nextEnd := strings.Index(input[end+1:], string(phClose)) |
| 225 | if nextEnd < 0 { |
| 226 | unclosedCount++ |
| 227 | continue scan |
| 228 | } |
| 229 | end += nextEnd + 1 |
| 230 | } |
| 231 | |
| 232 | // write the substring from the last cursor to this point |
no test coverage detected