Skip to content

Commit

Permalink
Initialize rows lazily
Browse files Browse the repository at this point in the history
  • Loading branch information
lhecker committed Jun 3, 2023
1 parent 3cb78a4 commit 9fbe3a6
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 83 deletions.
53 changes: 36 additions & 17 deletions src/buffer/out/Row.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,6 @@ ROW::ROW(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, c
_attr{ rowWidth, fillAttribute },
_columnCount{ rowWidth }
{
if (_chars.data())
{
_init();
}
}

void ROW::SetWrapForced(const bool wrap) noexcept
Expand Down Expand Up @@ -118,13 +114,12 @@ LineRendition ROW::GetLineRendition() const noexcept
return _lineRendition;
}

// Routine Description:
// - Sets all properties of the ROW to default values
// Arguments:
// - Attr - The default attribute (color) to fill
// Return Value:
// - <none>
void ROW::Reset(const TextAttribute& attr)
bool ROW::IsInitialized() const noexcept
{
return _initialized;
}

void ROW::Reset(const TextAttribute& attr) noexcept
{
_charsHeap.reset();
_chars = { _charsBuffer, _columnCount };
Expand All @@ -134,13 +129,37 @@ void ROW::Reset(const TextAttribute& attr)
_lineRendition = LineRendition::SingleWidth;
_wrapForced = false;
_doubleBytePadded = false;
_init();
_initialized = false;
}

void ROW::_init() noexcept
void ROW::Initialize() noexcept
{
std::fill_n(_chars.begin(), _columnCount, UNICODE_SPACE);
// The usage of _charsBuffer instead of _chars is quite intentional, as it
// allows _safeReset() to first call Initialize() and then Discard(), ending up
// with a ROW that is both not initialized and also consists only of whitespace.
std::fill_n(_charsBuffer, _columnCount, UNICODE_SPACE);
std::iota(_charOffsets.begin(), _charOffsets.end(), uint16_t{ 0 });
_initialized = true;
}

void ROW::Initialize(const ROW& whitespaceRow) noexcept
{
assert(!_initialized);
FAIL_FAST_IF(_columnCount != whitespaceRow._columnCount);
memcpy(_charsBuffer, whitespaceRow._charsBuffer, CalculateBufferStride(_columnCount));
_initialized = true;
}

// This is somewhat of a weapon of last resort to get the ROW back into a state that doesn't break
// callers too much. It discards the row (= releases the memory) but still ensures that it at least
// contains proper whitespace text to avoid any invalid text from being visible or returned anywhere.
void ROW::_safeReset() noexcept
{
static constexpr TextAttribute emptyAttributes{};
// By first calling Initialize() and then Discard() we end up in a state
// where _initialized is false, but the contents are still sort of valid.
Initialize();
Reset(emptyAttributes);
}

void ROW::TransferAttributes(const til::small_rle<TextAttribute, uint16_t, 1>& attr, til::CoordType newWidth)
Expand Down Expand Up @@ -365,7 +384,7 @@ catch (...)
// Due to this function writing _charOffsets first, then calling _resizeChars (which may throw) and only then finally
// filling in _chars, we might end up in a situation were _charOffsets contains offsets outside of the _chars array.
// --> Restore this row to a known "okay"-state.
Reset(TextAttribute{});
_safeReset();
throw;
}

Expand Down Expand Up @@ -416,7 +435,7 @@ try
}
catch (...)
{
Reset(TextAttribute{});
_safeReset();
throw;
}

Expand Down Expand Up @@ -546,7 +565,7 @@ try
}
catch (...)
{
Reset(TextAttribute{});
_safeReset();
throw;
}

Expand Down
29 changes: 27 additions & 2 deletions src/buffer/out/Row.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,27 @@ struct RowWriteState
class ROW final
{
public:
// The implicit agreement between ROW and TextBuffer is that each ROW gets a
// `columns` sized array of `wchar_t` as its `charsBuffer` argument and a
// `columns + 1` sized array of `uint16_t` as its `charOffsetsBuffer` argument.
// The former is used as a scratch buffer fitting any BMP codepoints into the
// ROW without the need for extra heap allocations and the latter stores the
// column-to-start-of-chararacter association. It has 1 more entry than needed
// because that one serves as the past-the-end _chars pointer. The expectation
// is that these two arrays are given to ROW laid out in memory back-to-back.
//
// This method exists to make this agreement explicit and serve as a reminder.
static constexpr size_t CalculateBufferStride(size_t columns) noexcept
{
return columns * 4 + 2;
}

// This function returns the size of the first part (the `columns` sized `wchar_t` array).
static constexpr size_t CalculateCharsBufferSize(size_t columns) noexcept
{
return columns * 2;
}

ROW() = default;
ROW(wchar_t* charsBuffer, uint16_t* charOffsetsBuffer, uint16_t rowWidth, const TextAttribute& fillAttribute);

Expand All @@ -76,7 +97,10 @@ class ROW final
void SetLineRendition(const LineRendition lineRendition) noexcept;
LineRendition GetLineRendition() const noexcept;

void Reset(const TextAttribute& attr);
bool IsInitialized() const noexcept;
void Reset(const TextAttribute& attr) noexcept;
void Initialize() noexcept;
void Initialize(const ROW& whitespaceRow) noexcept;
void TransferAttributes(const til::small_rle<TextAttribute, uint16_t, 1>& attr, til::CoordType newWidth);

til::CoordType NavigateToPrevious(til::CoordType column) const noexcept;
Expand Down Expand Up @@ -179,7 +203,7 @@ class ROW final
uint16_t _uncheckedCharOffset(size_t col) const noexcept;
bool _uncheckedIsTrailer(size_t col) const noexcept;

void _init() noexcept;
void _safeReset() noexcept;
void _resizeChars(uint16_t colEndDirty, uint16_t chBegDirty, size_t chEndDirty, uint16_t chEndDirtyOld);

// These fields are a bit "wasteful", but it makes all this a bit more robust against
Expand Down Expand Up @@ -233,6 +257,7 @@ class ROW final
bool _wrapForced = false;
// Occurs when the user runs out of text to support a double byte character and we're forced to the next line
bool _doubleBytePadded = false;
bool _initialized = false;
};

#ifdef UNIT_TESTING
Expand Down
Loading

0 comments on commit 9fbe3a6

Please sign in to comment.