Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove TranslateUnicodeToOem and all related code #14745

Merged
merged 12 commits into from
Feb 28, 2023
74 changes: 0 additions & 74 deletions src/host/dbcs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,77 +177,3 @@ BOOL IsAvailableEastAsianCodePage(const UINT uiCodePage)
return false;
}
}

_Ret_range_(0, cbAnsi)
ULONG TranslateUnicodeToOem(_In_reads_(cchUnicode) PCWCHAR pwchUnicode,
const ULONG cchUnicode,
_Out_writes_bytes_(cbAnsi) PCHAR pchAnsi,
const ULONG cbAnsi,
_Out_ std::unique_ptr<IInputEvent>& partialEvent)
{
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const auto TmpUni = new (std::nothrow) WCHAR[cchUnicode];
if (TmpUni == nullptr)
{
return 0;
}

memcpy(TmpUni, pwchUnicode, cchUnicode * sizeof(WCHAR));

BYTE AsciiDbcs[2];
AsciiDbcs[1] = 0;

ULONG i, j;
for (i = 0, j = 0; i < cchUnicode && j < cbAnsi; i++, j++)
{
if (IsGlyphFullWidth(TmpUni[i]))
{
const auto NumBytes = sizeof(AsciiDbcs);
ConvertToOem(gci.CP, &TmpUni[i], 1, (LPSTR)&AsciiDbcs[0], NumBytes);
if (IsDBCSLeadByteConsole(AsciiDbcs[0], &gci.CPInfo))
{
if (j < cbAnsi - 1)
{ // -1 is safe DBCS in buffer
pchAnsi[j] = AsciiDbcs[0];
j++;
pchAnsi[j] = AsciiDbcs[1];
AsciiDbcs[1] = 0;
}
else
{
pchAnsi[j] = AsciiDbcs[0];
break;
}
}
else
{
pchAnsi[j] = AsciiDbcs[0];
AsciiDbcs[1] = 0;
}
}
else
{
ConvertToOem(gci.CP, &TmpUni[i], 1, &pchAnsi[j], 1);
}
}

if (AsciiDbcs[1])
{
try
{
auto keyEvent = std::make_unique<KeyEvent>();
if (keyEvent.get())
{
keyEvent->SetCharData(AsciiDbcs[1]);
partialEvent.reset(static_cast<IInputEvent* const>(keyEvent.release()));
}
}
catch (...)
{
LOG_HR(wil::ResultFromCaughtException());
}
}

delete[] TmpUni;
return j;
}
7 changes: 0 additions & 7 deletions src/host/dbcs.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,3 @@ bool IsDBCSLeadByteConsole(const CHAR ch, const CPINFO* const pCPInfo);
BYTE CodePageToCharSet(const UINT uiCodePage);

BOOL IsAvailableEastAsianCodePage(const UINT uiCodePage);

_Ret_range_(0, cbAnsi)
ULONG TranslateUnicodeToOem(_In_reads_(cchUnicode) PCWCHAR pwchUnicode,
const ULONG cchUnicode,
_Out_writes_bytes_(cbAnsi) PCHAR pchAnsi,
const ULONG cbAnsi,
_Out_ std::unique_ptr<IInputEvent>& partialEvent);
38 changes: 21 additions & 17 deletions src/host/directio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,20 +169,30 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,
LockConsole();
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });

auto amountToRead = eventReadCount;

std::deque<std::unique_ptr<IInputEvent>> partialEvents;
if (!IsUnicode)
{
if (inputBuffer.IsReadPartialByteSequenceAvailable())
std::string buffer(eventReadCount, '\0');
gsl::span writer{ buffer };

if (IsPeek)
{
partialEvents.push_back(inputBuffer.FetchReadPartialByteSequence(IsPeek));
inputBuffer.PeekCachedA(writer);
}
else
{
inputBuffer.ConsumeCachedA(writer);
}
}

size_t amountToRead;
if (FAILED(SizeTSub(eventReadCount, partialEvents.size(), &amountToRead)))
{
return STATUS_INTEGER_OVERFLOW;
const auto read = buffer.size() - writer.size();
std::string_view str{ buffer.data(), read };
StringToInputEvents(str, partialEvents);

amountToRead -= read;
}

std::deque<std::unique_ptr<IInputEvent>> readEvents;
auto Status = inputBuffer.Read(readEvents,
amountToRead,
Expand All @@ -193,7 +203,6 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,

if (CONSOLE_STATUS_WAIT == Status)
{
FAIL_FAST_IF(!(readEvents.empty()));
// If we're told to wait until later, move all of our context
// to the read data object and send it back up to the server.
waiter = std::make_unique<DirectReadData>(&inputBuffer,
Expand All @@ -206,11 +215,7 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,
// split key events to oem chars if necessary
if (!IsUnicode)
{
try
{
SplitToOem(readEvents);
}
CATCH_LOG();
SplitToOem(readEvents);
}

// combine partial and readEvents
Expand All @@ -231,12 +236,11 @@ void EventsToUnicode(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& inEvents,
readEvents.pop_front();
}

// store partial event if necessary
if (!readEvents.empty())
{
inputBuffer.StoreReadPartialByteSequence(std::move(readEvents.front()));
readEvents.pop_front();
FAIL_FAST_IF(!(readEvents.empty()));
std::string buffer;
InputEventsToString(readEvents, buffer);
inputBuffer.CacheA(buffer);
}
}
return Status;
Expand Down
151 changes: 111 additions & 40 deletions src/host/inputBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
#include "stream.h"
#include "../types/inc/GlyphWidth.hpp"

#include <functional>
#include <til/bytes.h>

#include "misc.h"
#include "../interactivity/inc/ServiceLocator.hpp"

#define INPUT_BUFFER_DEFAULT_INPUT_MODE (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT)
Expand Down Expand Up @@ -36,59 +37,129 @@ InputBuffer::InputBuffer() :
fInComposition = false;
}

// Routine Description:
// - This routine frees the resources associated with an input buffer.
// Arguments:
// - None
// Return Value:
InputBuffer::~InputBuffer() = default;

// Routine Description:
// - checks if any partial char data is available for reading operation
// Arguments:
// - None
// Return Value:
// - true if partial char data is available, false otherwise
bool InputBuffer::IsReadPartialByteSequenceAvailable()
// Transfer as many `wchar_t`s from source over to the `wchar_t` buffer `target`. After it returns,
// the start of the `source` and `target` slices will be offset by as many bytes as have been copied
// over, so that if you call this function again it'll continue copying from wherever it left off.
// This function is intended for use with our W APIs.
void InputBuffer::ConsumeW(std::wstring_view& source, gsl::span<char>& target)
{
return _readPartialByteSequence.get() != nullptr;
if (!_aBuffer.empty())
{
_resetBufferA();
}

if (source.empty() || target.empty())
{
return;
}

til::bytes_transfer(target, source);
}

// Routine Description:
// - reads any read partial char data available
// Arguments:
// - peek - if true, data will not be removed after being fetched
// Return Value:
// - the partial char data. may be nullptr if no data is available
std::unique_ptr<IInputEvent> InputBuffer::FetchReadPartialByteSequence(_In_ bool peek)
// Transfer as many `wchar_t`s from source over to the `char` buffer `target`. After it returns,
// the start of the `source` and `target` slices will be offset by as many bytes as have been copied
// over, so that if you call this function again it'll continue copying from wherever it left off.
// This function is intended for use with our A APIs and performs the necessary `WideCharToMultiByte`
// conversion. Since not all converted `char`s might fit into `target` it'll cache the remainder.
// The next time this function is called those cached `char`s will then be the first to be copied over.
void InputBuffer::ConsumeA(std::wstring_view& source, gsl::span<char>& target)
{
if (!IsReadPartialByteSequenceAvailable())
// `_aBufferReader` might still contain target data from a previous invocation.
ConsumeCachedA(target);

if (source.empty() || target.empty())
{
return std::unique_ptr<IInputEvent>();
return;
}

if (peek)
// The above block should either leave `target` or `_aBufferReader` empty (or both).
// If we're here, `_aBufferReader` should be empty.
assert(_aBufferReader.empty());

const auto cp = ServiceLocator::LocateGlobals().getConsoleInformation().CP;

// Fast path: Batch convert all data in case the user provided buffer is large enough.
{
return IInputEvent::Create(_readPartialByteSequence->ToInputRecord());
const auto wideLength = gsl::narrow<ULONG>(source.size());
const auto narrowLength = gsl::narrow<ULONG>(target.size());

const auto length = WideCharToMultiByte(cp, 0, source.data(), wideLength, target.data(), narrowLength, nullptr, nullptr);
if (length > 0)
{
source = {};
til::bytes_advance(target, gsl::narrow_cast<size_t>(length));
return;
}

const auto error = GetLastError();
THROW_HR_IF(HRESULT_FROM_WIN32(error), error != ERROR_INSUFFICIENT_BUFFER);
}
else

// Slow path: Character-wise conversion otherwise. We do this in order to only
// consume as many characters from `source` as necessary to fill `target`.
{
std::unique_ptr<IInputEvent> outEvent;
outEvent.swap(_readPartialByteSequence);
return outEvent;
size_t read = 0;

for (const auto& wch : source)
{
char buffer[8];
const auto length = WideCharToMultiByte(cp, 0, &wch, 1, &buffer[0], sizeof(buffer), nullptr, nullptr);
THROW_LAST_ERROR_IF(length <= 0);

std::string_view slice{ &buffer[0], gsl::narrow_cast<size_t>(length) };
til::bytes_transfer(target, slice);

++read;

if (!slice.empty())
{
_aBuffer = slice;
_aBufferReader = _aBuffer;
break;
}
}

source = source.substr(read);
}
}

// Routine Description:
// - stores partial read char data for a later read. will overwrite
// any previously stored data.
// Arguments:
// - event - The event to store
// Return Value:
// - None
void InputBuffer::StoreReadPartialByteSequence(std::unique_ptr<IInputEvent> event)
// Same as `ConsumeA`, but without any `source` characters.
void InputBuffer::ConsumeCachedA(gsl::span<char>& target)
{
if (_aBufferReader.empty())
{
return;
}

til::bytes_transfer(target, _aBufferReader);

if (_aBufferReader.empty())
{
// This is just so that we release memory eagerly.
_aBuffer = std::string{};
}
}

// Same as `ConsumeCachedA`, but as the name indicates it doesn't "consume" the amount of data that has been copied
// into `target`. Still, the `target` slice will have been offset the same way `ConsumeW` and `ConsumeA` do it.
void InputBuffer::PeekCachedA(gsl::span<char>& target)
{
auto reader = _aBufferReader;
til::bytes_transfer(target, reader);
}

// This function allows you to manually add some data into the internal buffer used by `ConsumeA`, etc.
void InputBuffer::CacheA(std::string_view source)
{
const auto off = _aBufferReader.size() - _aBuffer.size();
_aBuffer.append(source);
_aBufferReader = std::string_view{ _aBuffer }.substr(off);
}

void InputBuffer::_resetBufferA()
{
_readPartialByteSequence.swap(event);
_aBuffer = std::string{};
_aBufferReader = {};
}

// Routine Description:
Expand Down
17 changes: 11 additions & 6 deletions src/host/inputBuffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ class InputBuffer final : public ConsoleObjectHeader
bool fInComposition; // specifies if there's an ongoing text composition

InputBuffer();
~InputBuffer();

// storage API for partial dbcs bytes being read from the buffer
bool IsReadPartialByteSequenceAvailable();
std::unique_ptr<IInputEvent> FetchReadPartialByteSequence(_In_ bool peek);
void StoreReadPartialByteSequence(std::unique_ptr<IInputEvent> event);
void ConsumeW(std::wstring_view& source, gsl::span<char>& target);

void ConsumeA(std::wstring_view& source, gsl::span<char>& target);
void ConsumeCachedA(gsl::span<char>& target);
void PeekCachedA(gsl::span<char>& target);
void CacheA(std::string_view source);

// storage API for partial dbcs bytes being written to the buffer
bool IsWritePartialByteSequenceAvailable();
Expand Down Expand Up @@ -84,8 +85,10 @@ class InputBuffer final : public ConsoleObjectHeader
void PassThroughWin32MouseRequest(bool enable);

private:
std::string _aBuffer;
std::string_view _aBufferReader;

std::deque<std::unique_ptr<IInputEvent>> _storage;
std::unique_ptr<IInputEvent> _readPartialByteSequence;
std::unique_ptr<IInputEvent> _writePartialByteSequence;
Microsoft::Console::VirtualTerminal::TerminalInput _termInput;
Microsoft::Console::Render::VtEngine* _pTtyConnection;
Expand All @@ -96,6 +99,8 @@ class InputBuffer final : public ConsoleObjectHeader
// Otherwise, we should be calling them.
bool _vtInputShouldSuppress{ false };

void _resetBufferA();

void _ReadBuffer(_Out_ std::deque<std::unique_ptr<IInputEvent>>& outEvents,
const size_t readCount,
_Out_ size_t& eventsRead,
Expand Down
Loading