Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
lhecker committed Nov 23, 2023
1 parent c423ae2 commit a66a018
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 131 deletions.
9 changes: 7 additions & 2 deletions src/host/directio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,15 @@ using Microsoft::Console::Interactivity::ServiceLocator;
LockConsole();
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });

const auto count = inputBuffer.Read(IsUnicode, IsPeek, outEvents, *eventReadCount);
const InputBuffer::ReadDescriptor readDesc{
.wide = IsUnicode,
.records = true,
.peek = IsPeek,
};
const auto count = inputBuffer.Read(readDesc, outEvents, *eventReadCount * sizeof(INPUT_RECORD));
if (count)
{
*eventReadCount = count;
*eventReadCount = count / sizeof(INPUT_RECORD);
return S_OK;
}

Expand Down
162 changes: 75 additions & 87 deletions src/host/inputBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,17 +325,12 @@ void InputBuffer::FlushAllButKeys()
{
}

size_t InputBuffer::Read(bool wide, bool peek, void* data, const size_t capacity)
size_t InputBuffer::Read(ReadDescriptor desc, void* data, size_t capacityInBytes)
{
if (!wide)
{
return 0;
}

auto remaining = capacity;
auto out = static_cast<wchar_t*>(data);
auto remaining = capacityInBytes;
const auto unitSize = desc.records ? sizeof(INPUT_RECORD) : (desc.wide ? 2 : 1);

while (remaining > 0)
/*while (remaining > unitSize)
{
const auto span = _spans.peek();
if (!span)
Expand All @@ -346,75 +341,28 @@ size_t InputBuffer::Read(bool wide, bool peek, void* data, const size_t capacity
switch (span->type)
{
case SpanType::Record:
{
while (remaining > 0 && span->length > 0)
{
const auto r = _records.peek();
if (!r)
{
break;
}
if (r->EventType == KEY_EVENT && r->Event.KeyEvent.bKeyDown && r->Event.KeyEvent.uChar.UnicodeChar)
{
*out++ = r->Event.KeyEvent.uChar.UnicodeChar;
remaining--;
}
_records.advance(1);
span->length--;
}
if (!span->length)
{
_spans.advance(1);
}
break;
}
case SpanType::Text:
{
const auto can = std::min(remaining, span->length);
out += _text.read(out, can);
data += _records.read(data, can);
remaining -= can;
span->length -= can;
if (!span->length)
{
_spans.advance(1);
}
break;
}
}
}

return capacity - remaining;
}

size_t InputBuffer::Read(bool wide, bool peek, INPUT_RECORD* data, const size_t capacity)
{
if (!wide)
{
return 0;
}

auto remaining = capacity;

while (remaining > 0)
{
const auto span = _spans.peek();
if (!span)
{
break;
}

switch (span->type)
{
case SpanType::Record:
{
const auto can = std::min(remaining, span->length);

data += _records.read(data, can);
remaining -= can;
span->length -= can;
while (remaining > 0 && span->length > 0)
{
const auto r = _records.peek();
if (!r)
{
break;
}
_records.advance(1);
span->length--;
}
if (!span->length)
{
_spans.advance(1);
Expand Down Expand Up @@ -442,9 +390,9 @@ size_t InputBuffer::Read(bool wide, bool peek, INPUT_RECORD* data, const size_t
break;
}
}
}
}*/

return capacity - remaining;
return capacityInBytes - remaining;
}

void InputBuffer::Write(const INPUT_RECORD& record)
Expand All @@ -453,45 +401,85 @@ void InputBuffer::Write(const INPUT_RECORD& record)
}

void InputBuffer::Write(const std::span<const INPUT_RECORD>& records)
try
{
if (!records.empty())
if (records.empty())
{
return;
}

const auto initiallyEmpty = _bufferWriter == _bufferReader;

for (const auto& r : records)
{
_records.write(records.data(), records.size());
_writeSpan(SpanType::Record, records.size());
*_allocateRecord() = r;
}

if (initiallyEmpty)
{
ServiceLocator::LocateGlobals().hInputEvent.SetEvent();
}
WakeUpReadersWaitingForData();
}
CATCH_LOG()

void InputBuffer::Write(const std::wstring_view& text)
try
{
if (!text.empty())
if (text.empty())
{
_text.write(text.data(), text.size());
_writeSpan(SpanType::Text, text.size());
return;
}
}
CATCH_LOG()

void InputBuffer::_writeSpan(SpanType type, size_t length)
{
auto lastSpan = _spans.last_written();
const auto initiallyEmpty = !lastSpan;
const auto initiallyEmpty = _bufferWriter == _bufferReader;

if (!lastSpan || lastSpan->type != type)
if (!initiallyEmpty && text.size() < 9)
{
_spans.write({ type, 0 });
lastSpan = _spans.last_written();
auto& last = _buffer[(_bufferWriter + _bufferMask) & _bufferMask];
if (last.EventType >= 0xff00 && (last.EventType + text.size()) <= 0xff09)
{
auto& lastp = *reinterpret_cast<InputBufferTextChunk*>(&last);

Check failure on line 441 in src/host/inputBuffer.cpp

View workflow job for this annotation

GitHub Actions / Spell checking

`lastp` is not a recognized word. (unrecognized-spelling)
}
}

lastSpan->length += length;

if (initiallyEmpty)
{
ServiceLocator::LocateGlobals().hInputEvent.SetEvent();
}

WakeUpReadersWaitingForData();
}
CATCH_LOG()

INPUT_RECORD* InputBuffer::_allocateRecord()
{
// Use a virtual ring buffer
// Use segments with length header
// Keep a pointer to the last segment

if ((_bufferWriter + 1) & _bufferMask == _bufferReader)
{
const auto oldCap = _bufferMask + 1;
const auto newCap = std::max(size_t{ 128 }, oldCap * 2);
auto newBuf = std::make_unique_for_overwrite<INPUT_RECORD[]>(newCap);

const auto src1 = _buffer.get() + _bufferReader;
const auto len1 = oldCap - _bufferReader;
const auto src2 = _buffer.get();
const auto len2 = _bufferWriter;
memcpy(newBuf.get(), src1, len1);
memcpy(newBuf.get() + len1, src2, len2);

_buffer = std::move(newBuf);
_bufferReader = 0;
_bufferWriter = len1 + len2;
}

const auto ptr = _buffer.get() + _bufferWriter;

_bufferWriter = (_bufferWriter + 1) & _bufferMask;

return ptr;
}

// This can be considered a "privileged" variant of Write() which allows FOCUS_EVENTs to generate focus VT sequences.
// If we didn't do this, someone could write a FOCUS_EVENT_RECORD with WriteConsoleInput, exit without flushing the
Expand Down
24 changes: 18 additions & 6 deletions src/host/inputBuffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ namespace Microsoft::Console::Render
class VtEngine;
}

struct InputBufferTextChunk
{
WORD EventType; // = 0xff00 + amount of characters stored in buffer
wchar_t buffer[9];
};

class InputBuffer final : public ConsoleObjectHeader
{
public:
Expand Down Expand Up @@ -45,8 +51,13 @@ class InputBuffer final : public ConsoleObjectHeader
void Flush();
void FlushAllButKeys();

size_t Read(bool wide, bool peek, void* data, size_t capacity);
size_t Read(bool wide, bool peek, INPUT_RECORD* data, size_t capacity);
struct ReadDescriptor
{
bool wide;
bool records;
bool peek;
};
size_t Read(ReadDescriptor desc, void* data, size_t capacityInBytes);

void Write(const INPUT_RECORD& record);
void Write(const std::span<const INPUT_RECORD>& records);
Expand Down Expand Up @@ -85,15 +96,16 @@ class InputBuffer final : public ConsoleObjectHeader
std::deque<INPUT_RECORD> _cachedInputEvents;
ReadingMode _readingMode = ReadingMode::StringA;

til::ring_buffer<Span> _spans;
til::ring_buffer<INPUT_RECORD> _records;
til::ring_buffer<wchar_t> _text;
std::unique_ptr<INPUT_RECORD[]> _buffer;
size_t _bufferMask = 0;
size_t _bufferReader = 0;
size_t _bufferWriter = 0;

INPUT_RECORD _writePartialByteSequence{};
bool _writePartialByteSequenceAvailable = false;
Microsoft::Console::VirtualTerminal::TerminalInput _termInput;

void _writeSpan(SpanType type, size_t length);
INPUT_RECORD* _allocateRecord();
void _switchReadingMode(ReadingMode mode);
void _switchReadingModeSlowPath(ReadingMode mode);
void _WriteBuffer(const std::span<const INPUT_RECORD>& inRecords, _Out_ size_t& eventsWritten, _Out_ bool& setWaitEvent);
Expand Down
10 changes: 7 additions & 3 deletions src/host/readDataCooked.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,9 @@ size_t COOKED_READ_DATA::_wordNext(const std::wstring_view& chars, size_t positi
void COOKED_READ_DATA::_readCharInputLoop()
{
wchar_t buffer[128];
InputBuffer::ReadDescriptor readDesc{
.wide = true,
};

while (_state == State::Accumulating)
{
Expand All @@ -404,8 +407,8 @@ void COOKED_READ_DATA::_readCharInputLoop()
//const auto pPopupKeys = hasPopup ? &popupKeys : nullptr;
DWORD modifiers = 0;

const auto count = _pInputBuffer->Read(true, false, &buffer, 128);
if (count == 0)
const auto bytes = _pInputBuffer->Read(readDesc, &buffer, sizeof(buffer));
if (bytes == 0)
{
break;
}
Expand All @@ -424,7 +427,8 @@ void COOKED_READ_DATA::_readCharInputLoop()
}
else
{
for (size_t i = 0; i < count; ++i)
const auto c = bytes / 2;
for (size_t i = 0; i < c; ++i)
_handleChar(buffer[i], modifiers);
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/host/readDataDirect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,17 @@ try
return true;
}

const auto count = _pInputBuffer->Read(fIsUnicode, false, pOutputData, _eventReadCount);
const InputBuffer::ReadDescriptor readDesc{
.wide = fIsUnicode,
.records = true,
};
const auto count = _pInputBuffer->Read(readDesc, pOutputData, _eventReadCount * sizeof(INPUT_RECORD));
if (!count)
{
return false;
}

*pNumBytes = count * sizeof(INPUT_RECORD);
*pNumBytes = count;
return true;
}
catch (...)
Expand Down
28 changes: 5 additions & 23 deletions src/host/stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,31 +262,13 @@ NT_CATCH_RETURN()
try
{
UNREFERENCED_PARAMETER(readHandleState);

bytesRead = 0;

auto cap = buffer.size();
if (unicode)
{
cap /= 2;
}

const auto count = inputBuffer.Read(unicode, false, buffer.data(), cap);
if (count == 0)
{
return CONSOLE_STATUS_WAIT;
}

// Once we read some data off the InputBuffer it can't be read again, so we
// need to make sure to return a success status to the client in that case.
auto bytes = count;
if (unicode)
{
bytes *= 2;
}

bytesRead = bytes;
return STATUS_SUCCESS;
const InputBuffer::ReadDescriptor readDesc{
.wide = unicode,
};
bytesRead = inputBuffer.Read(readDesc, buffer.data(), buffer.size());
return bytesRead == 0 ? CONSOLE_STATUS_WAIT : STATUS_SUCCESS;
}
NT_CATCH_RETURN()

Expand Down
Loading

1 comment on commit a66a018

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@check-spelling-bot Report

🔴 Please review

See the 📜action log for details.

Unrecognized words (2)

atvis
lastp

Previously acknowledged words that are now absent CProc expectedinput NONCONST rgi rgui spammy traceloggingprovider userbase VProc VRaw wcsnicmp :arrow_right:
To accept ✔️ these unrecognized words as correct and remove the previously acknowledged and now absent words, run the following commands

... in a clone of the git@github.com:PKRoma/Terminal.git repository
on the dev/lhecker/ring-buffer-input-buffer branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/v0.0.21/apply.pl' |
perl - 'https://github.com/PKRoma/Terminal/actions/runs/6979772774/attempts/1'
Pattern suggestions ✂️ (1)

You could add these patterns to .github/actions/spelling/patterns/a66a018b452dc8ceb8935003eb81024a4e3cd5a3.txt:

# Automatically suggested patterns
# hit-count: 1 file-count: 1
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...
\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b

Warnings (1)

See the 📜action log for details.

ℹ️ Warnings Count
ℹ️ candidate-pattern 1

See ℹ️ Event descriptions for more information.

✏️ Contributor please read this

By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.

⚠️ The command is written for posix shells. If it doesn't work for you, you can manually add (one word per line) / remove items to expect.txt and the excludes.txt files.

If the listed items are:

  • ... misspelled, then please correct them instead of using the command.
  • ... names, please add them to .github/actions/spelling/allow/names.txt.
  • ... APIs, you can add them to a file in .github/actions/spelling/allow/.
  • ... just things you're using, please add them to an appropriate file in .github/actions/spelling/expect/.
  • ... tokens you only need in one place and shouldn't generally be used, you can add an item in an appropriate file in .github/actions/spelling/patterns/.

See the README.md in each directory for more information.

🔬 You can test your commits without appending to a PR by creating a new branch with that extra change and pushing it to your fork. The check-spelling action will run in response to your push -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. 😉

If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Please sign in to comment.