private boolean performReplacement() throws IOException {
        boolean doneReplacement = false;
        inBuffer.fillBuffer();
        final CharSequence charSequence = new PaddingWrapper(inBuffer, inputOffset != 0);
        final Matcher matcher = pattern.matcher(charSequence);

        if (matcher.find(inputOffset)) {
            final int start = matcher.start();
            final int end = matcher.end();

            // Guard against matching up to the end of the visible buffer unless we have reached the end of the file.
            if (start <= bufferSize || inBuffer.isEof()) {
                final boolean matchedBufferStart = start == 0;
                final boolean matchedBufferEnd = end == charSequence.length() && !inBuffer.isEof();

                if (matchedBufferStart && matchedBufferEnd) {
                    // If we matched all text in the buffer but aren't actually at the start of the stream or
                    // at the end then this is not likely to be correct.
                    error("The pattern matched all text in the buffer. Consider changing your match expression " +
                            "or making the buffer bigger.");
                } else if (matchedBufferEnd) {
                    if (!inBuffer.isEof()) {
                        // If we matched to the end of the buffer and we are not at the end of the stream then
                        // this is not likely to be correct.
                        error("The pattern matched text at the end of the buffer when we are not at the end of " +
                                "the stream. Consider changing your match expression or making the buffer bigger");
                    }
                } else {
                    // Append the replacement. Note that we do this with a separate buffer as we don't actually
                    // want to keep the start of the string that occurs before the `lastPosition` that is added
                    // by appendReplacement. We are using `lastPosition` in our matches so that matches after
                    // the first do not match a start anchor.
                    matcher.appendReplacement(replacementBuffer, replacement);
                    outBuffer.append(replacementBuffer, inputOffset, replacementBuffer.length());
                    replacementBuffer.setLength(0);

                    // Move to the end of the match minus one char.
                    final int advance = end - inputOffset;
                    if (advance > 0) {
                        move(Math.min(inBuffer.length(), advance));
                    } else {
                        // Copy a single char to at least advance rather than getting stuck at this position.
                        // This mimics the behaviour of`replaceAll()`.
                        copyBuffer(Math.min(inBuffer.length(), 1));
                    }

                    doneReplacement = true;
                    replacementCount++;
                    totalReplacementCount++;

                    // Stop any further replacements if we got to our max replacement count or didn't advance
                    // and are at the end of the input.
                    if (replacementCount == maxReplacements
                            || (advance == 0 && inBuffer.isEof() && inBuffer.length() == 0)) {
                        allowReplacement = false;
                    }
                }
            }
        }

        if (!doneReplacement) {
            // We didn't manage to replace anything then copy the buffered text to the output.
            if (inBuffer.isEof()) {
                // Copy all remaining text to the output buffer.
                copyBuffer(inBuffer.length());
            } else {
                // Copy the current buffer text up to a maximum length of buffer size.
                copyBuffer(Math.min(inBuffer.length(), bufferSize));
            }
        }

        // Subsequent matches will work on padded input so that `^` anchored patterns will no longer match.
        inputOffset = 1;
        return doneReplacement;
    }
Read text into the input buffer, replace the first match if possible and copy replaced text to the output buffer.
stroom-pipeline/src/main/java/stroom/pipeline/reader/FindReplaceFilter.java