Repository: microsoft/terminal
File: src/host/readDataCooked.cpp (C++)

Code before:
            // split the contents of a multiline alias (for instance `doskey test=echo foo$Techo bar$Techo baz`)
            // into multiple separate read outputs, ensuring that the client receives them line by line.
            //
            // This code first truncates the `input` to only contain the first line, so that Consume() below only
            // writes that line into the user buffer. We'll later store the remainder in SaveMultilinePendingInput().
            if (LineCount > 1)
            {
                // ProcessAliases() is supposed to end each line with \r\n. If it doesn't we might as well fail-fast.
                const auto firstLineEnd = input.find(UNICODE_LINEFEED) + 1;
                input = input.substr(0, firstLineEnd);
            }
        }
    }

    const auto inputSizeBefore = input.size();
    GetInputBuffer()->Consume(isUnicode, input, writer);

    if (LineCount > 1)
    {
        // This is a continuation of the above identical if condition.
        // We've truncated the `input` slice and now we need to restore it.
        const auto inputSizeAfter = input.size();
        const auto amountConsumed = inputSizeBefore - inputSizeAfter;
        input = { _backupLimit, _bytesRead / sizeof(wchar_t) };
        input = input.substr(amountConsumed);
        GetInputReadHandleData()->SaveMultilinePendingInput(input);
    }
    else if (!input.empty())
    {
        GetInputReadHandleData()->SavePendingInput(input);
    }

    numBytes = _userBufferSize - writer.size();
    controlKeyState = _controlKeyState;
    return STATUS_SUCCESS;
}

void COOKED_READ_DATA::MigrateUserBuffersOnTransitionToBackgroundWait(const void* oldBuffer, void* newBuffer)
{
    // See the comment in WaitBlock.cpp for more information.
    if (_userBuffer == oldBuffer)
    {
        _userBuffer = static_cast<char*>(newBuffer);
    }
}


Review comment (suggestion):
This isn't necessarily the most readable code I've written so far... But it does do the correct thing: After `Consume()` returns, `input` might not be empty! So if we restore the original, full `input` slice we need to manually calculate the amount of data that was read (`amountConsumed`).

An alternative I've considered is to change `Consume()` to return the amount of characters that were read, but given how `Consume()` works internally, this would be identical to this `inputSizeBefore` / `inputSizeAfter` code. Alternatively we could pass a "character limit" argument, but that would be, once again, similar to the existing code, since string-views are already strings with a limit, so I felt like passing 2 limits wouldn't be any less confusing.

Code after:
            // split the contents of a multiline alias (for instance `doskey test=echo foo$Techo bar$Techo baz`)
            // into multiple separate read outputs, ensuring that the client receives them line by line.
            //
            // This code first truncates the `input` to only contain the first line, so that Consume() below only
            // writes that line into the user buffer. We'll later store the remainder in SaveMultilinePendingInput().
            if (LineCount > 1)
            {
                // ProcessAliases() is supposed to end each line with \r\n. If it doesn't we might as well fail-fast.
                const auto firstLineEnd = input.find(UNICODE_LINEFEED) + 1;
                input = input.substr(0, std::min(input.size(), firstLineEnd));
            }
        }
    }

    const auto inputSizeBefore = input.size();
    GetInputBuffer()->Consume(isUnicode, input, writer);

    if (LineCount > 1)
    {
        // This is a continuation of the above identical if condition.
        // We've truncated the `input` slice and now we need to restore it.
        const auto inputSizeAfter = input.size();
        const auto amountConsumed = inputSizeBefore - inputSizeAfter;
        input = { _backupLimit, _bytesRead / sizeof(wchar_t) };
        input = input.substr(std::min(input.size(), amountConsumed));
        GetInputReadHandleData()->SaveMultilinePendingInput(input);
    }
    else if (!input.empty())
    {
        GetInputReadHandleData()->SavePendingInput(input);
    }

    numBytes = _userBufferSize - writer.size();
    controlKeyState = _controlKeyState;
    return STATUS_SUCCESS;
}

void COOKED_READ_DATA::MigrateUserBuffersOnTransitionToBackgroundWait(const void* oldBuffer, void* newBuffer)
{
    // See the comment in WaitBlock.cpp for more information.
    if (_userBuffer == oldBuffer)
    {
        _userBuffer = static_cast<char*>(newBuffer);
    }
}