Greatly reduce allocations in the conhost/OpenConsole startup path (#8489)
I was looking at conhost/OpenConsole and noticed it was being pretty inefficient with allocations due to some usages of std::deque and std::vector that didn't need to be done quite that way. So this uses std::vector for the TextBuffer's storage of ROW objects, which allows one allocation to contiguously reserve space for all the ROWs - on Desktop this is 9001 ROW objects which means it saves 9000 allocations that the std::deque would have done. Plus it has the benefit of increasing locality of the ROW objects since deque is going to chase pointers more often with its data structure. Then, within each ROW there are CharRow and ATTR_ROW objects that use std::vector today. This changes them to use Boost's small_vector, which is a variation of vector that allows for the so-called "small string optimization." Since we know the typical size of these vectors, we can pre-reserve the right number of elements directly in the CharRow/ATTR_ROW instances, avoiding any heap allocations at all for constructing these objects. There are a ton of variations on this "small_vector" concept out there in the world - this one in Boost, LLVM has one called SmallVector, Electronic Arts' STL has a small_vector, Facebook's folly library has one...there are a silly number of these out there. But Boost seems like it's by far the easiest to consume in terms of integration into this repo, the CI/CD pipeline, licensing, and stuff like that, so I went with the boost version. In terms of numbers, I measured the startup path of OpenConsole.exe on my dev box for Release x64 configuration. My box is an i7-6700k @ 4 Ghz, with 32 GB RAM, not that I think machine config matters much here: | | Allocation count | Allocated bytes | CPU usage (ms) | | ------ | ------------------- | ------------------ | -------------- | | Before | 29,461 | 4,984,640 | 103 | | After | 2,459 (-91%) | 4,853,931 (-2.6%) | 96 (-7%) | Along the way, I also fixed a dynamic initializer I happened to spot in the registry code, and updated some docs. ## Validation Steps Performed - Ran "runut", "runft" and "runuia" locally and confirmed results are the same as the main branch - Profiled the before/after numbers in the Visual Studio profiler, for the numbers shown in the table Co-authored-by: Austin Lamb <austinl@microsoft.com>
This commit is contained in:
parent
551cc9a98b
commit
539a5dc0af
14
doc/TAEF.md
14
doc/TAEF.md
|
@ -48,3 +48,17 @@ Invoke-OpenConsoleTests
|
|||
```
|
||||
|
||||
`Invoke-OpenConsoleTests` supports a number of options, which you can enumerate by running `Invoke-OpenConsoleTests -?`.
|
||||
|
||||
|
||||
### Debugging Tests
|
||||
|
||||
If you want to debug a test, you can do so by using the TAEF /waitForDebugger flag, such as:
|
||||
|
||||
runut *Tests.dll /name:TextBufferTests::TestInsertCharacter /waitForDebugger
|
||||
|
||||
Replace the test name with the one you want to debug. Then, TAEF will begin executing the test and output something like this:
|
||||
|
||||
TAEF: Waiting for debugger - PID <some PID> @ IP <some IP address>
|
||||
|
||||
You can then attach to that PID in your debugger of choice. In Visual Studio, you can use Debug -> Attach To Process, or you could use WinDbg or whatever you want.
|
||||
Once the debugger attaches, the test will execute and your breakpoints will be hit.
|
||||
|
|
|
@ -11,10 +11,16 @@
|
|||
// - attr - the default text attribute
|
||||
// Return Value:
|
||||
// - constructed object
|
||||
// Note: will throw exception if unable to allocate memory for text attribute storage
|
||||
ATTR_ROW::ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
|
||||
ATTR_ROW::ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr) noexcept
|
||||
{
|
||||
_list.push_back(TextAttributeRun(cchRowWidth, attr));
|
||||
try
|
||||
{
|
||||
_list.emplace_back(TextAttributeRun(cchRowWidth, attr));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
FAIL_FAST_CAUGHT_EXCEPTION();
|
||||
}
|
||||
_cchRowWidth = cchRowWidth;
|
||||
}
|
||||
|
||||
|
@ -25,7 +31,7 @@ ATTR_ROW::ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
|
|||
void ATTR_ROW::Reset(const TextAttribute attr)
|
||||
{
|
||||
_list.clear();
|
||||
_list.push_back(TextAttributeRun(_cchRowWidth, attr));
|
||||
_list.emplace_back(TextAttributeRun(_cchRowWidth, attr));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
|
@ -402,7 +408,7 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt
|
|||
// The original run was 3 long. The insertion run was 1 long. We need 1 more for the
|
||||
// fact that an existing piece of the run was split in half (to hold the latter half).
|
||||
const size_t cNewRun = _list.size() + newAttrs.size() + 1;
|
||||
std::vector<TextAttributeRun> newRun;
|
||||
decltype(_list) newRun;
|
||||
newRun.reserve(cNewRun);
|
||||
|
||||
// We will start analyzing from the beginning of our existing run.
|
||||
|
@ -595,8 +601,7 @@ std::vector<TextAttributeRun> ATTR_ROW::PackAttrs(const std::vector<TextAttribut
|
|||
{
|
||||
if (runs.empty() || runs.back().GetAttributes() != attr)
|
||||
{
|
||||
const TextAttributeRun run(1, attr);
|
||||
runs.push_back(run);
|
||||
runs.emplace_back(TextAttributeRun(1, attr));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -20,6 +20,7 @@ Revision History:
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "boost/container/small_vector.hpp"
|
||||
#include "TextAttributeRun.hpp"
|
||||
#include "AttrRowIterator.hpp"
|
||||
|
||||
|
@ -28,7 +29,16 @@ class ATTR_ROW final
|
|||
public:
|
||||
using const_iterator = typename AttrRowIterator;
|
||||
|
||||
ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr);
|
||||
ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
|
||||
noexcept;
|
||||
|
||||
~ATTR_ROW() = default;
|
||||
|
||||
ATTR_ROW(const ATTR_ROW&) = default;
|
||||
ATTR_ROW& operator=(const ATTR_ROW&) = default;
|
||||
ATTR_ROW(ATTR_ROW&&)
|
||||
noexcept = default;
|
||||
ATTR_ROW& operator=(ATTR_ROW&&) noexcept = default;
|
||||
|
||||
void Reset(const TextAttribute attr);
|
||||
|
||||
|
@ -65,7 +75,7 @@ public:
|
|||
friend class AttrRowIterator;
|
||||
|
||||
private:
|
||||
std::vector<TextAttributeRun> _list;
|
||||
boost::container::small_vector<TextAttributeRun, 1> _list;
|
||||
size_t _cchRowWidth;
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
|
|
|
@ -39,19 +39,6 @@ bool AttrRowIterator::operator!=(const AttrRowIterator& it) const noexcept
|
|||
return !(*this == it);
|
||||
}
|
||||
|
||||
AttrRowIterator& AttrRowIterator::operator++() noexcept
|
||||
{
|
||||
_increment(1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AttrRowIterator AttrRowIterator::operator++(int) noexcept
|
||||
{
|
||||
auto copy = *this;
|
||||
_increment(1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
AttrRowIterator& AttrRowIterator::operator+=(const ptrdiff_t& movement)
|
||||
{
|
||||
if (!_exceeded)
|
||||
|
@ -74,19 +61,6 @@ AttrRowIterator& AttrRowIterator::operator-=(const ptrdiff_t& movement)
|
|||
return this->operator+=(-movement);
|
||||
}
|
||||
|
||||
AttrRowIterator& AttrRowIterator::operator--() noexcept
|
||||
{
|
||||
_decrement(1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
AttrRowIterator AttrRowIterator::operator--(int) noexcept
|
||||
{
|
||||
auto copy = *this;
|
||||
_decrement(1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
const TextAttribute* AttrRowIterator::operator->() const
|
||||
{
|
||||
THROW_HR_IF(E_BOUNDS, _exceeded);
|
||||
|
|
|
@ -15,6 +15,7 @@ Author(s):
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "boost/container/small_vector.hpp"
|
||||
#include "TextAttribute.hpp"
|
||||
#include "TextAttributeRun.hpp"
|
||||
|
||||
|
@ -38,20 +39,38 @@ public:
|
|||
bool operator==(const AttrRowIterator& it) const noexcept;
|
||||
bool operator!=(const AttrRowIterator& it) const noexcept;
|
||||
|
||||
AttrRowIterator& operator++() noexcept;
|
||||
AttrRowIterator operator++(int) noexcept;
|
||||
AttrRowIterator& operator++() noexcept
|
||||
{
|
||||
_increment(1);
|
||||
return *this;
|
||||
}
|
||||
AttrRowIterator operator++(int) noexcept
|
||||
{
|
||||
auto copy = *this;
|
||||
_increment(1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
AttrRowIterator& operator+=(const ptrdiff_t& movement);
|
||||
AttrRowIterator& operator-=(const ptrdiff_t& movement);
|
||||
|
||||
AttrRowIterator& operator--() noexcept;
|
||||
AttrRowIterator operator--(int) noexcept;
|
||||
AttrRowIterator& operator--() noexcept
|
||||
{
|
||||
_decrement(1);
|
||||
return *this;
|
||||
}
|
||||
AttrRowIterator operator--(int) noexcept
|
||||
{
|
||||
auto copy = *this;
|
||||
_decrement(1);
|
||||
return copy;
|
||||
}
|
||||
|
||||
const TextAttribute* operator->() const;
|
||||
const TextAttribute& operator*() const;
|
||||
|
||||
private:
|
||||
std::vector<TextAttributeRun>::const_iterator _run;
|
||||
boost::container::small_vector_base<TextAttributeRun>::const_iterator _run;
|
||||
const ATTR_ROW* _pAttrRow;
|
||||
size_t _currentAttributeIndex; // index of TextAttribute within the current TextAttributeRun
|
||||
bool _exceeded;
|
||||
|
|
|
@ -15,13 +15,16 @@
|
|||
// Return Value:
|
||||
// - instantiated object
|
||||
// Note: will through if unable to allocate char/attribute buffers
|
||||
CharRow::CharRow(size_t rowWidth, ROW* const pParent) :
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 26447) // small_vector's constructor says it can throw but it should not given how we use it. This suppresses this error for the AuditMode build.
|
||||
CharRow::CharRow(size_t rowWidth, ROW* const pParent) noexcept :
|
||||
_wrapForced{ false },
|
||||
_doubleBytePadded{ false },
|
||||
_data(rowWidth, value_type()),
|
||||
_pParent{ FAIL_FAST_IF_NULL(pParent) }
|
||||
{
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
// Routine Description:
|
||||
// - Sets the wrap status for the current row
|
||||
|
@ -141,7 +144,7 @@ typename CharRow::const_iterator CharRow::cend() const noexcept
|
|||
// - The calculated left boundary of the internal string.
|
||||
size_t CharRow::MeasureLeft() const noexcept
|
||||
{
|
||||
std::vector<value_type>::const_iterator it = _data.cbegin();
|
||||
const_iterator it = _data.cbegin();
|
||||
while (it != _data.cend() && it->IsSpace())
|
||||
{
|
||||
++it;
|
||||
|
@ -155,9 +158,9 @@ size_t CharRow::MeasureLeft() const noexcept
|
|||
// - <none>
|
||||
// Return Value:
|
||||
// - The calculated right boundary of the internal string.
|
||||
size_t CharRow::MeasureRight() const noexcept
|
||||
size_t CharRow::MeasureRight() const
|
||||
{
|
||||
std::vector<value_type>::const_reverse_iterator it = _data.crbegin();
|
||||
const_reverse_iterator it = _data.crbegin();
|
||||
while (it != _data.crend() && it->IsSpace())
|
||||
{
|
||||
++it;
|
||||
|
|
|
@ -24,6 +24,7 @@ Revision History:
|
|||
#include "CharRowCellReference.hpp"
|
||||
#include "CharRowCell.hpp"
|
||||
#include "UnicodeStorage.hpp"
|
||||
#include "boost/container/small_vector.hpp"
|
||||
|
||||
class ROW;
|
||||
|
||||
|
@ -49,11 +50,12 @@ class CharRow final
|
|||
public:
|
||||
using glyph_type = typename wchar_t;
|
||||
using value_type = typename CharRowCell;
|
||||
using iterator = typename std::vector<value_type>::iterator;
|
||||
using const_iterator = typename std::vector<value_type>::const_iterator;
|
||||
using iterator = typename boost::container::small_vector_base<value_type>::iterator;
|
||||
using const_iterator = typename boost::container::small_vector_base<value_type>::const_iterator;
|
||||
using const_reverse_iterator = typename boost::container::small_vector_base<value_type>::const_reverse_iterator;
|
||||
using reference = typename CharRowCellReference;
|
||||
|
||||
CharRow(size_t rowWidth, ROW* const pParent);
|
||||
CharRow(size_t rowWidth, ROW* const pParent) noexcept;
|
||||
|
||||
void SetWrapForced(const bool wrap) noexcept;
|
||||
bool WasWrapForced() const noexcept;
|
||||
|
@ -63,7 +65,7 @@ public:
|
|||
void Reset() noexcept;
|
||||
[[nodiscard]] HRESULT Resize(const size_t newSize) noexcept;
|
||||
size_t MeasureLeft() const noexcept;
|
||||
size_t MeasureRight() const noexcept;
|
||||
size_t MeasureRight() const;
|
||||
void ClearCell(const size_t column);
|
||||
bool ContainsText() const noexcept;
|
||||
const DbcsAttribute& DbcsAttrAt(const size_t column) const;
|
||||
|
@ -101,7 +103,7 @@ protected:
|
|||
bool _doubleBytePadded;
|
||||
|
||||
// storage for glyph data and dbcs attributes
|
||||
std::vector<value_type> _data;
|
||||
boost::container::small_vector<value_type, 120> _data;
|
||||
|
||||
// ROW that this CharRow belongs to
|
||||
ROW* _pParent;
|
||||
|
|
|
@ -8,18 +8,6 @@
|
|||
// default glyph value, used for resetting the character data portion of a cell
|
||||
static constexpr wchar_t DefaultValue = UNICODE_SPACE;
|
||||
|
||||
CharRowCell::CharRowCell() noexcept :
|
||||
_wch{ DefaultValue },
|
||||
_attr{}
|
||||
{
|
||||
}
|
||||
|
||||
CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept :
|
||||
_wch{ wch },
|
||||
_attr{ attr }
|
||||
{
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - "erases" the glyph. really sets it back to the default "empty" value
|
||||
void CharRowCell::EraseChars() noexcept
|
||||
|
|
|
@ -17,6 +17,7 @@ Author(s):
|
|||
#pragma once
|
||||
|
||||
#include "DbcsAttribute.hpp"
|
||||
#include "unicode.hpp"
|
||||
|
||||
#if (defined(_M_IX86) || defined(_M_AMD64))
|
||||
// currently CharRowCell's fields use 3 bytes of memory, leaving the 4th byte in unused. this leads
|
||||
|
@ -27,8 +28,13 @@ Author(s):
|
|||
class CharRowCell final
|
||||
{
|
||||
public:
|
||||
CharRowCell() noexcept;
|
||||
CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept;
|
||||
CharRowCell() noexcept = default;
|
||||
CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept
|
||||
:
|
||||
_wch(wch),
|
||||
_attr(attr)
|
||||
{
|
||||
}
|
||||
|
||||
void EraseChars() noexcept;
|
||||
void Reset() noexcept;
|
||||
|
@ -44,8 +50,8 @@ public:
|
|||
friend constexpr bool operator==(const CharRowCell& a, const CharRowCell& b) noexcept;
|
||||
|
||||
private:
|
||||
wchar_t _wch;
|
||||
DbcsAttribute _attr;
|
||||
wchar_t _wch{ UNICODE_SPACE };
|
||||
DbcsAttribute _attr{};
|
||||
};
|
||||
|
||||
#if (defined(_M_IX86) || defined(_M_AMD64))
|
||||
|
|
|
@ -32,8 +32,8 @@ public:
|
|||
}
|
||||
|
||||
~CharRowCellReference() = default;
|
||||
CharRowCellReference(const CharRowCellReference&) = default;
|
||||
CharRowCellReference(CharRowCellReference&&) = default;
|
||||
CharRowCellReference(const CharRowCellReference&) noexcept = default;
|
||||
CharRowCellReference(CharRowCellReference&&) noexcept = default;
|
||||
|
||||
void operator=(const CharRowCellReference&) = delete;
|
||||
void operator=(CharRowCellReference&&) = delete;
|
||||
|
|
|
@ -16,50 +16,15 @@
|
|||
// - pParent - the text buffer that this row belongs to
|
||||
// Return Value:
|
||||
// - constructed object
|
||||
ROW::ROW(const SHORT rowId, const short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent) :
|
||||
ROW::ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent) noexcept :
|
||||
_id{ rowId },
|
||||
_rowWidth{ gsl::narrow<size_t>(rowWidth) },
|
||||
_charRow{ gsl::narrow<size_t>(rowWidth), this },
|
||||
_attrRow{ gsl::narrow<UINT>(rowWidth), fillAttribute },
|
||||
_rowWidth{ rowWidth },
|
||||
_charRow{ rowWidth, this },
|
||||
_attrRow{ rowWidth, fillAttribute },
|
||||
_pParent{ pParent }
|
||||
{
|
||||
}
|
||||
|
||||
size_t ROW::size() const noexcept
|
||||
{
|
||||
return _rowWidth;
|
||||
}
|
||||
|
||||
const CharRow& ROW::GetCharRow() const noexcept
|
||||
{
|
||||
return _charRow;
|
||||
}
|
||||
|
||||
CharRow& ROW::GetCharRow() noexcept
|
||||
{
|
||||
return _charRow;
|
||||
}
|
||||
|
||||
const ATTR_ROW& ROW::GetAttrRow() const noexcept
|
||||
{
|
||||
return _attrRow;
|
||||
}
|
||||
|
||||
ATTR_ROW& ROW::GetAttrRow() noexcept
|
||||
{
|
||||
return _attrRow;
|
||||
}
|
||||
|
||||
SHORT ROW::GetId() const noexcept
|
||||
{
|
||||
return _id;
|
||||
}
|
||||
|
||||
void ROW::SetId(const SHORT id) noexcept
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Sets all properties of the ROW to default values
|
||||
// Arguments:
|
||||
|
@ -87,7 +52,7 @@ bool ROW::Reset(const TextAttribute Attr)
|
|||
// - width - the new width, in cells
|
||||
// Return Value:
|
||||
// - S_OK if successful, otherwise relevant error
|
||||
[[nodiscard]] HRESULT ROW::Resize(const size_t width)
|
||||
[[nodiscard]] HRESULT ROW::Resize(const unsigned short width)
|
||||
{
|
||||
RETURN_IF_FAILED(_charRow.Resize(width));
|
||||
try
|
||||
|
@ -113,25 +78,6 @@ void ROW::ClearColumn(const size_t column)
|
|||
_charRow.ClearCell(column);
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - gets the text of the row as it would be shown on the screen
|
||||
// Return Value:
|
||||
// - wstring containing text for the row
|
||||
std::wstring ROW::GetText() const
|
||||
{
|
||||
return _charRow.GetText();
|
||||
}
|
||||
|
||||
RowCellIterator ROW::AsCellIter(const size_t startIndex) const
|
||||
{
|
||||
return AsCellIter(startIndex, size() - startIndex);
|
||||
}
|
||||
|
||||
RowCellIterator ROW::AsCellIter(const size_t startIndex, const size_t count) const
|
||||
{
|
||||
return RowCellIterator(*this, startIndex, count);
|
||||
}
|
||||
|
||||
UnicodeStorage& ROW::GetUnicodeStorage() noexcept
|
||||
{
|
||||
return _pParent->GetUnicodeStorage();
|
||||
|
|
|
@ -32,27 +32,28 @@ class TextBuffer;
|
|||
class ROW final
|
||||
{
|
||||
public:
|
||||
ROW(const SHORT rowId, const short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent);
|
||||
ROW(const SHORT rowId, const unsigned short rowWidth, const TextAttribute fillAttribute, TextBuffer* const pParent)
|
||||
noexcept;
|
||||
|
||||
size_t size() const noexcept;
|
||||
size_t size() const noexcept { return _rowWidth; }
|
||||
|
||||
const CharRow& GetCharRow() const noexcept;
|
||||
CharRow& GetCharRow() noexcept;
|
||||
const CharRow& GetCharRow() const noexcept { return _charRow; }
|
||||
CharRow& GetCharRow() noexcept { return _charRow; }
|
||||
|
||||
const ATTR_ROW& GetAttrRow() const noexcept;
|
||||
ATTR_ROW& GetAttrRow() noexcept;
|
||||
const ATTR_ROW& GetAttrRow() const noexcept { return _attrRow; }
|
||||
ATTR_ROW& GetAttrRow() noexcept { return _attrRow; }
|
||||
|
||||
SHORT GetId() const noexcept;
|
||||
void SetId(const SHORT id) noexcept;
|
||||
SHORT GetId() const noexcept { return _id; }
|
||||
void SetId(const SHORT id) noexcept { _id = id; }
|
||||
|
||||
bool Reset(const TextAttribute Attr);
|
||||
[[nodiscard]] HRESULT Resize(const size_t width);
|
||||
[[nodiscard]] HRESULT Resize(const unsigned short width);
|
||||
|
||||
void ClearColumn(const size_t column);
|
||||
std::wstring GetText() const;
|
||||
std::wstring GetText() const { return _charRow.GetText(); }
|
||||
|
||||
RowCellIterator AsCellIter(const size_t startIndex) const;
|
||||
RowCellIterator AsCellIter(const size_t startIndex, const size_t count) const;
|
||||
RowCellIterator AsCellIter(const size_t startIndex) const { return AsCellIter(startIndex, size() - startIndex); }
|
||||
RowCellIterator AsCellIter(const size_t startIndex, const size_t count) const { return RowCellIterator(*this, startIndex, count); }
|
||||
|
||||
UnicodeStorage& GetUnicodeStorage() noexcept;
|
||||
const UnicodeStorage& GetUnicodeStorage() const noexcept;
|
||||
|
@ -69,7 +70,7 @@ private:
|
|||
CharRow _charRow;
|
||||
ATTR_ROW _attrRow;
|
||||
SHORT _id;
|
||||
size_t _rowWidth;
|
||||
unsigned short _rowWidth;
|
||||
TextBuffer* _pParent; // non ownership pointer
|
||||
};
|
||||
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include "precomp.h"
|
||||
#include "TextAttributeRun.hpp"
|
||||
|
||||
TextAttributeRun::TextAttributeRun() noexcept :
|
||||
_cchLength(0)
|
||||
{
|
||||
SetAttributes(TextAttribute(0));
|
||||
}
|
||||
|
||||
TextAttributeRun::TextAttributeRun(const size_t cchLength, const TextAttribute attr) noexcept :
|
||||
_cchLength(cchLength)
|
||||
{
|
||||
SetAttributes(attr);
|
||||
}
|
||||
|
||||
size_t TextAttributeRun::GetLength() const noexcept
|
||||
{
|
||||
return _cchLength;
|
||||
}
|
||||
|
||||
void TextAttributeRun::SetLength(const size_t cchLength) noexcept
|
||||
{
|
||||
_cchLength = cchLength;
|
||||
}
|
||||
|
||||
void TextAttributeRun::IncrementLength() noexcept
|
||||
{
|
||||
_cchLength++;
|
||||
}
|
||||
|
||||
void TextAttributeRun::DecrementLength() noexcept
|
||||
{
|
||||
_cchLength--;
|
||||
}
|
||||
|
||||
const TextAttribute& TextAttributeRun::GetAttributes() const noexcept
|
||||
{
|
||||
return _attributes;
|
||||
}
|
||||
|
||||
void TextAttributeRun::SetAttributes(const TextAttribute textAttribute) noexcept
|
||||
{
|
||||
_attributes = textAttribute;
|
||||
}
|
|
@ -25,20 +25,24 @@ Revision History:
|
|||
class TextAttributeRun final
|
||||
{
|
||||
public:
|
||||
TextAttributeRun() noexcept;
|
||||
TextAttributeRun(const size_t cchLength, const TextAttribute attr) noexcept;
|
||||
TextAttributeRun() = default;
|
||||
TextAttributeRun(const size_t cchLength, const TextAttribute attr) noexcept :
|
||||
_cchLength(gsl::narrow<unsigned int>(cchLength))
|
||||
{
|
||||
SetAttributes(attr);
|
||||
}
|
||||
|
||||
size_t GetLength() const noexcept;
|
||||
void SetLength(const size_t cchLength) noexcept;
|
||||
void IncrementLength() noexcept;
|
||||
void DecrementLength() noexcept;
|
||||
size_t GetLength() const noexcept { return _cchLength; }
|
||||
void SetLength(const size_t cchLength) noexcept { _cchLength = gsl::narrow<unsigned int>(cchLength); }
|
||||
void IncrementLength() noexcept { _cchLength++; }
|
||||
void DecrementLength() noexcept { _cchLength--; }
|
||||
|
||||
const TextAttribute& GetAttributes() const noexcept;
|
||||
void SetAttributes(const TextAttribute textAttribute) noexcept;
|
||||
const TextAttribute& GetAttributes() const noexcept { return _attributes; }
|
||||
void SetAttributes(const TextAttribute textAttribute) noexcept { _attributes = textAttribute; }
|
||||
|
||||
private:
|
||||
size_t _cchLength;
|
||||
TextAttribute _attributes;
|
||||
unsigned int _cchLength{ 0 };
|
||||
TextAttribute _attributes{ 0 };
|
||||
|
||||
#ifdef UNIT_TESTING
|
||||
friend class AttrRowTests;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<RootNamespace>bufferout</RootNamespace>
|
||||
<ProjectName>BufferOut</ProjectName>
|
||||
<TargetName>ConBufferOut</TargetName>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)src\common.build.pre.props" />
|
||||
<ItemGroup>
|
||||
|
@ -22,7 +22,6 @@
|
|||
<ClCompile Include="..\search.cpp" />
|
||||
<ClCompile Include="..\TextColor.cpp" />
|
||||
<ClCompile Include="..\TextAttribute.cpp" />
|
||||
<ClCompile Include="..\TextAttributeRun.cpp" />
|
||||
<ClCompile Include="..\textBuffer.cpp" />
|
||||
<ClCompile Include="..\textBufferCellIterator.cpp" />
|
||||
<ClCompile Include="..\textBufferTextIterator.cpp" />
|
||||
|
@ -61,4 +60,4 @@
|
|||
</ItemGroup>
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="$(SolutionDir)src\common.build.post.props" />
|
||||
</Project>
|
||||
</Project>
|
|
@ -40,7 +40,6 @@ SOURCES= \
|
|||
..\RowCellIterator.cpp \
|
||||
..\TextColor.cpp \
|
||||
..\TextAttribute.cpp \
|
||||
..\TextAttributeRun.cpp \
|
||||
..\textBuffer.cpp \
|
||||
..\textBufferCellIterator.cpp \
|
||||
..\textBufferTextIterator.cpp \
|
||||
|
|
|
@ -42,6 +42,7 @@ TextBuffer::TextBuffer(const COORD screenBufferSize,
|
|||
_currentPatternId{ 0 }
|
||||
{
|
||||
// initialize ROWs
|
||||
_storage.reserve(static_cast<size_t>(screenBufferSize.Y));
|
||||
for (size_t i = 0; i < static_cast<size_t>(screenBufferSize.Y); ++i)
|
||||
{
|
||||
_storage.emplace_back(static_cast<SHORT>(i), screenBufferSize.X, _currentAttributes, this);
|
||||
|
@ -837,11 +838,10 @@ void TextBuffer::Reset()
|
|||
const SHORT TopRowIndex = (GetFirstRowIndex() + TopRow) % currentSize.Y;
|
||||
|
||||
// rotate rows until the top row is at index 0
|
||||
const ROW& newTopRow = _storage.at(TopRowIndex);
|
||||
while (&newTopRow != &_storage.front())
|
||||
for (int i = 0; i < TopRowIndex; i++)
|
||||
{
|
||||
_storage.push_back(std::move(_storage.front()));
|
||||
_storage.pop_front();
|
||||
_storage.emplace_back(std::move(_storage.front()));
|
||||
_storage.erase(_storage.begin());
|
||||
}
|
||||
|
||||
_SetFirstRowIndex(0);
|
||||
|
@ -2410,7 +2410,7 @@ PointTree TextBuffer::GetPatterns(const size_t firstRow, const size_t lastRow) c
|
|||
// all the text into one string and find the patterns in that string
|
||||
for (auto i = firstRow; i <= lastRow; ++i)
|
||||
{
|
||||
auto row = GetRowByOffset(i);
|
||||
auto& row = GetRowByOffset(i);
|
||||
concatAll += row.GetCharRow().GetText();
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ filling in the last row, and updating the screen.
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "cursor.h"
|
||||
#include "Row.hpp"
|
||||
#include "TextAttribute.hpp"
|
||||
|
@ -190,7 +192,7 @@ public:
|
|||
private:
|
||||
void _UpdateSize();
|
||||
Microsoft::Console::Types::Viewport _size;
|
||||
std::deque<ROW> _storage;
|
||||
std::vector<ROW> _storage;
|
||||
Cursor _cursor;
|
||||
|
||||
SHORT _firstRow; // indexes top row (not necessarily 0)
|
||||
|
|
|
@ -329,7 +329,7 @@ void Terminal::UpdateSettings(ICoreSettings settings)
|
|||
{
|
||||
try
|
||||
{
|
||||
auto row = newTextBuffer->GetRowByOffset(::base::ClampSub(proposedTop, 1));
|
||||
auto& row = newTextBuffer->GetRowByOffset(::base::ClampSub(proposedTop, 1));
|
||||
if (row.GetCharRow().WasWrapForced())
|
||||
{
|
||||
proposedTop--;
|
||||
|
|
|
@ -3529,7 +3529,7 @@ void ConptyRoundtripTests::HyperlinkIdConsistency()
|
|||
|
||||
auto verifyData = [](TextBuffer& tb) {
|
||||
// Check that all the linked cells still have the same ID
|
||||
auto attrRow = tb.GetRowByOffset(0).GetAttrRow();
|
||||
auto& attrRow = tb.GetRowByOffset(0).GetAttrRow();
|
||||
auto id = attrRow.GetAttrByColumn(0).GetHyperlinkId();
|
||||
for (auto i = 1; i < 4; ++i)
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="AuditMode|Win32">
|
||||
<ProjectConfiguration Include="AuditMode|Win32">
|
||||
<Configuration>AuditMode</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
|
@ -90,7 +90,7 @@
|
|||
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;$(SolutionDir)\oss\interval_tree;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\src\inc;$(SolutionDir)\dep;$(SolutionDir)\dep\Console;$(SolutionDir)\dep\Win32K;$(SolutionDir)\dep\gsl\include;$(SolutionDir)\dep\wil\include;$(SolutionDir)\oss\chromium;$(SolutionDir)\oss\fmt\include;$(SolutionDir)\oss\dynamic_bitset;$(SolutionDir)\oss\libpopcnt;$(SolutionDir)\oss\interval_tree;$(SolutionDir)\oss\boost\boost_1_73_0;%(AdditionalIncludeDirectories);</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<RuntimeTypeInfo>false</RuntimeTypeInfo>
|
||||
|
|
|
@ -88,6 +88,8 @@ TRACELOGGING_DECLARE_PROVIDER(g_hConhostV2EventTraceProvider);
|
|||
#include "../inc/operators.hpp"
|
||||
#include "../inc/conattrs.hpp"
|
||||
|
||||
#include "boost/container/small_vector.hpp"
|
||||
|
||||
// TODO: MSFT 9355094 Find a better way of doing this. http://osgvsowi/9355094
|
||||
[[nodiscard]] inline NTSTATUS NTSTATUS_FROM_HRESULT(HRESULT hr)
|
||||
{
|
||||
|
|
|
@ -235,6 +235,24 @@ class AttrRowTests
|
|||
return NoThrowString().Format(L"%wc%d", run.GetAttributes().GetLegacyAttributes(), run.GetLength());
|
||||
}
|
||||
|
||||
void LogChain(_In_ PCWSTR pwszPrefix,
|
||||
boost::container::small_vector_base<TextAttributeRun>& chain)
|
||||
{
|
||||
NoThrowString str(pwszPrefix);
|
||||
|
||||
if (chain.size() > 0)
|
||||
{
|
||||
str.Append(LogRunElement(chain[0]));
|
||||
|
||||
for (size_t i = 1; i < chain.size(); i++)
|
||||
{
|
||||
str.AppendFormat(L"->%s", (const wchar_t*)(LogRunElement(chain[i])));
|
||||
}
|
||||
}
|
||||
|
||||
Log::Comment(str);
|
||||
}
|
||||
|
||||
void LogChain(_In_ PCWSTR pwszPrefix,
|
||||
std::vector<TextAttributeRun>& chain)
|
||||
{
|
||||
|
|
|
@ -80,19 +80,10 @@ public:
|
|||
|
||||
typedef struct _RegPropertyMap
|
||||
{
|
||||
_RegPropertyType const propertyType;
|
||||
_RegPropertyType propertyType;
|
||||
PCWSTR pwszValueName;
|
||||
DWORD const dwFieldOffset;
|
||||
size_t const cbFieldSize;
|
||||
_RegPropertyMap(
|
||||
_RegPropertyType const propertyType,
|
||||
PCWSTR pwszValueName,
|
||||
DWORD const dwFieldOffset,
|
||||
size_t const cbFieldSize) :
|
||||
propertyType(propertyType),
|
||||
pwszValueName(pwszValueName),
|
||||
dwFieldOffset(dwFieldOffset),
|
||||
cbFieldSize(cbFieldSize){};
|
||||
DWORD dwFieldOffset;
|
||||
size_t cbFieldSize;
|
||||
|
||||
_RegPropertyMap& operator=(const _RegPropertyMap&) { return *this; }
|
||||
} RegPropertyMap;
|
||||
|
|
Loading…
Reference in New Issue