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:
Austin Lamb 2020-12-16 10:40:30 -08:00 committed by GitHub
parent 551cc9a98b
commit 539a5dc0af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 158 additions and 222 deletions

View file

@ -48,3 +48,17 @@ Invoke-OpenConsoleTests
``` ```
`Invoke-OpenConsoleTests` supports a number of options, which you can enumerate by running `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.

View file

@ -11,10 +11,16 @@
// - attr - the default text attribute // - attr - the default text attribute
// Return Value: // Return Value:
// - constructed object // - 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) noexcept
ATTR_ROW::ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
{ {
_list.push_back(TextAttributeRun(cchRowWidth, attr)); try
{
_list.emplace_back(TextAttributeRun(cchRowWidth, attr));
}
catch (...)
{
FAIL_FAST_CAUGHT_EXCEPTION();
}
_cchRowWidth = cchRowWidth; _cchRowWidth = cchRowWidth;
} }
@ -25,7 +31,7 @@ ATTR_ROW::ATTR_ROW(const UINT cchRowWidth, const TextAttribute attr)
void ATTR_ROW::Reset(const TextAttribute attr) void ATTR_ROW::Reset(const TextAttribute attr)
{ {
_list.clear(); _list.clear();
_list.push_back(TextAttributeRun(_cchRowWidth, attr)); _list.emplace_back(TextAttributeRun(_cchRowWidth, attr));
} }
// Routine Description: // 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 // 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). // 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; const size_t cNewRun = _list.size() + newAttrs.size() + 1;
std::vector<TextAttributeRun> newRun; decltype(_list) newRun;
newRun.reserve(cNewRun); newRun.reserve(cNewRun);
// We will start analyzing from the beginning of our existing run. // 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) if (runs.empty() || runs.back().GetAttributes() != attr)
{ {
const TextAttributeRun run(1, attr); runs.emplace_back(TextAttributeRun(1, attr));
runs.push_back(run);
} }
else else
{ {

View file

@ -20,6 +20,7 @@ Revision History:
#pragma once #pragma once
#include "boost/container/small_vector.hpp"
#include "TextAttributeRun.hpp" #include "TextAttributeRun.hpp"
#include "AttrRowIterator.hpp" #include "AttrRowIterator.hpp"
@ -28,7 +29,16 @@ class ATTR_ROW final
public: public:
using const_iterator = typename AttrRowIterator; 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); void Reset(const TextAttribute attr);
@ -65,7 +75,7 @@ public:
friend class AttrRowIterator; friend class AttrRowIterator;
private: private:
std::vector<TextAttributeRun> _list; boost::container::small_vector<TextAttributeRun, 1> _list;
size_t _cchRowWidth; size_t _cchRowWidth;
#ifdef UNIT_TESTING #ifdef UNIT_TESTING

View file

@ -39,19 +39,6 @@ bool AttrRowIterator::operator!=(const AttrRowIterator& it) const noexcept
return !(*this == it); 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) AttrRowIterator& AttrRowIterator::operator+=(const ptrdiff_t& movement)
{ {
if (!_exceeded) if (!_exceeded)
@ -74,19 +61,6 @@ AttrRowIterator& AttrRowIterator::operator-=(const ptrdiff_t& movement)
return this->operator+=(-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 const TextAttribute* AttrRowIterator::operator->() const
{ {
THROW_HR_IF(E_BOUNDS, _exceeded); THROW_HR_IF(E_BOUNDS, _exceeded);

View file

@ -15,6 +15,7 @@ Author(s):
#pragma once #pragma once
#include "boost/container/small_vector.hpp"
#include "TextAttribute.hpp" #include "TextAttribute.hpp"
#include "TextAttributeRun.hpp" #include "TextAttributeRun.hpp"
@ -38,20 +39,38 @@ public:
bool operator==(const AttrRowIterator& it) const noexcept; bool operator==(const AttrRowIterator& it) const noexcept;
bool operator!=(const AttrRowIterator& it) const noexcept; bool operator!=(const AttrRowIterator& it) const noexcept;
AttrRowIterator& operator++() noexcept; AttrRowIterator& operator++() noexcept
AttrRowIterator operator++(int) 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-=(const ptrdiff_t& movement); AttrRowIterator& operator-=(const ptrdiff_t& movement);
AttrRowIterator& operator--() noexcept; AttrRowIterator& operator--() noexcept
AttrRowIterator operator--(int) noexcept; {
_decrement(1);
return *this;
}
AttrRowIterator operator--(int) noexcept
{
auto copy = *this;
_decrement(1);
return copy;
}
const TextAttribute* operator->() const; const TextAttribute* operator->() const;
const TextAttribute& operator*() const; const TextAttribute& operator*() const;
private: private:
std::vector<TextAttributeRun>::const_iterator _run; boost::container::small_vector_base<TextAttributeRun>::const_iterator _run;
const ATTR_ROW* _pAttrRow; const ATTR_ROW* _pAttrRow;
size_t _currentAttributeIndex; // index of TextAttribute within the current TextAttributeRun size_t _currentAttributeIndex; // index of TextAttribute within the current TextAttributeRun
bool _exceeded; bool _exceeded;

View file

@ -15,13 +15,16 @@
// Return Value: // Return Value:
// - instantiated object // - instantiated object
// Note: will through if unable to allocate char/attribute buffers // 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 }, _wrapForced{ false },
_doubleBytePadded{ false }, _doubleBytePadded{ false },
_data(rowWidth, value_type()), _data(rowWidth, value_type()),
_pParent{ FAIL_FAST_IF_NULL(pParent) } _pParent{ FAIL_FAST_IF_NULL(pParent) }
{ {
} }
#pragma warning(pop)
// Routine Description: // Routine Description:
// - Sets the wrap status for the current row // - 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. // - The calculated left boundary of the internal string.
size_t CharRow::MeasureLeft() const noexcept 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()) while (it != _data.cend() && it->IsSpace())
{ {
++it; ++it;
@ -155,9 +158,9 @@ size_t CharRow::MeasureLeft() const noexcept
// - <none> // - <none>
// Return Value: // Return Value:
// - The calculated right boundary of the internal string. // - 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()) while (it != _data.crend() && it->IsSpace())
{ {
++it; ++it;

View file

@ -24,6 +24,7 @@ Revision History:
#include "CharRowCellReference.hpp" #include "CharRowCellReference.hpp"
#include "CharRowCell.hpp" #include "CharRowCell.hpp"
#include "UnicodeStorage.hpp" #include "UnicodeStorage.hpp"
#include "boost/container/small_vector.hpp"
class ROW; class ROW;
@ -49,11 +50,12 @@ class CharRow final
public: public:
using glyph_type = typename wchar_t; using glyph_type = typename wchar_t;
using value_type = typename CharRowCell; using value_type = typename CharRowCell;
using iterator = typename std::vector<value_type>::iterator; using iterator = typename boost::container::small_vector_base<value_type>::iterator;
using const_iterator = typename std::vector<value_type>::const_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; 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; void SetWrapForced(const bool wrap) noexcept;
bool WasWrapForced() const noexcept; bool WasWrapForced() const noexcept;
@ -63,7 +65,7 @@ public:
void Reset() noexcept; void Reset() noexcept;
[[nodiscard]] HRESULT Resize(const size_t newSize) noexcept; [[nodiscard]] HRESULT Resize(const size_t newSize) noexcept;
size_t MeasureLeft() const noexcept; size_t MeasureLeft() const noexcept;
size_t MeasureRight() const noexcept; size_t MeasureRight() const;
void ClearCell(const size_t column); void ClearCell(const size_t column);
bool ContainsText() const noexcept; bool ContainsText() const noexcept;
const DbcsAttribute& DbcsAttrAt(const size_t column) const; const DbcsAttribute& DbcsAttrAt(const size_t column) const;
@ -101,7 +103,7 @@ protected:
bool _doubleBytePadded; bool _doubleBytePadded;
// storage for glyph data and dbcs attributes // 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 that this CharRow belongs to
ROW* _pParent; ROW* _pParent;

View file

@ -8,18 +8,6 @@
// default glyph value, used for resetting the character data portion of a cell // default glyph value, used for resetting the character data portion of a cell
static constexpr wchar_t DefaultValue = UNICODE_SPACE; 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: // Routine Description:
// - "erases" the glyph. really sets it back to the default "empty" value // - "erases" the glyph. really sets it back to the default "empty" value
void CharRowCell::EraseChars() noexcept void CharRowCell::EraseChars() noexcept

View file

@ -17,6 +17,7 @@ Author(s):
#pragma once #pragma once
#include "DbcsAttribute.hpp" #include "DbcsAttribute.hpp"
#include "unicode.hpp"
#if (defined(_M_IX86) || defined(_M_AMD64)) #if (defined(_M_IX86) || defined(_M_AMD64))
// currently CharRowCell's fields use 3 bytes of memory, leaving the 4th byte in unused. this leads // 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 class CharRowCell final
{ {
public: public:
CharRowCell() noexcept; CharRowCell() noexcept = default;
CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept; CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept
:
_wch(wch),
_attr(attr)
{
}
void EraseChars() noexcept; void EraseChars() noexcept;
void Reset() noexcept; void Reset() noexcept;
@ -44,8 +50,8 @@ public:
friend constexpr bool operator==(const CharRowCell& a, const CharRowCell& b) noexcept; friend constexpr bool operator==(const CharRowCell& a, const CharRowCell& b) noexcept;
private: private:
wchar_t _wch; wchar_t _wch{ UNICODE_SPACE };
DbcsAttribute _attr; DbcsAttribute _attr{};
}; };
#if (defined(_M_IX86) || defined(_M_AMD64)) #if (defined(_M_IX86) || defined(_M_AMD64))

View file

@ -32,8 +32,8 @@ public:
} }
~CharRowCellReference() = default; ~CharRowCellReference() = default;
CharRowCellReference(const CharRowCellReference&) = default; CharRowCellReference(const CharRowCellReference&) noexcept = default;
CharRowCellReference(CharRowCellReference&&) = default; CharRowCellReference(CharRowCellReference&&) noexcept = default;
void operator=(const CharRowCellReference&) = delete; void operator=(const CharRowCellReference&) = delete;
void operator=(CharRowCellReference&&) = delete; void operator=(CharRowCellReference&&) = delete;

View file

@ -16,50 +16,15 @@
// - pParent - the text buffer that this row belongs to // - pParent - the text buffer that this row belongs to
// Return Value: // Return Value:
// - constructed object // - 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 }, _id{ rowId },
_rowWidth{ gsl::narrow<size_t>(rowWidth) }, _rowWidth{ rowWidth },
_charRow{ gsl::narrow<size_t>(rowWidth), this }, _charRow{ rowWidth, this },
_attrRow{ gsl::narrow<UINT>(rowWidth), fillAttribute }, _attrRow{ rowWidth, fillAttribute },
_pParent{ pParent } _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: // Routine Description:
// - Sets all properties of the ROW to default values // - Sets all properties of the ROW to default values
// Arguments: // Arguments:
@ -87,7 +52,7 @@ bool ROW::Reset(const TextAttribute Attr)
// - width - the new width, in cells // - width - the new width, in cells
// Return Value: // Return Value:
// - S_OK if successful, otherwise relevant error // - 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)); RETURN_IF_FAILED(_charRow.Resize(width));
try try
@ -113,25 +78,6 @@ void ROW::ClearColumn(const size_t column)
_charRow.ClearCell(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 UnicodeStorage& ROW::GetUnicodeStorage() noexcept
{ {
return _pParent->GetUnicodeStorage(); return _pParent->GetUnicodeStorage();

View file

@ -32,27 +32,28 @@ class TextBuffer;
class ROW final class ROW final
{ {
public: 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; const CharRow& GetCharRow() const noexcept { return _charRow; }
CharRow& GetCharRow() noexcept; CharRow& GetCharRow() noexcept { return _charRow; }
const ATTR_ROW& GetAttrRow() const noexcept; const ATTR_ROW& GetAttrRow() const noexcept { return _attrRow; }
ATTR_ROW& GetAttrRow() noexcept; ATTR_ROW& GetAttrRow() noexcept { return _attrRow; }
SHORT GetId() const noexcept; SHORT GetId() const noexcept { return _id; }
void SetId(const SHORT id) noexcept; void SetId(const SHORT id) noexcept { _id = id; }
bool Reset(const TextAttribute Attr); 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); 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 { return AsCellIter(startIndex, size() - startIndex); }
RowCellIterator AsCellIter(const size_t startIndex, const size_t count) const; RowCellIterator AsCellIter(const size_t startIndex, const size_t count) const { return RowCellIterator(*this, startIndex, count); }
UnicodeStorage& GetUnicodeStorage() noexcept; UnicodeStorage& GetUnicodeStorage() noexcept;
const UnicodeStorage& GetUnicodeStorage() const noexcept; const UnicodeStorage& GetUnicodeStorage() const noexcept;
@ -69,7 +70,7 @@ private:
CharRow _charRow; CharRow _charRow;
ATTR_ROW _attrRow; ATTR_ROW _attrRow;
SHORT _id; SHORT _id;
size_t _rowWidth; unsigned short _rowWidth;
TextBuffer* _pParent; // non ownership pointer TextBuffer* _pParent; // non ownership pointer
}; };

View file

@ -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;
}

View file

@ -25,20 +25,24 @@ Revision History:
class TextAttributeRun final class TextAttributeRun final
{ {
public: public:
TextAttributeRun() noexcept; TextAttributeRun() = default;
TextAttributeRun(const size_t cchLength, const TextAttribute attr) noexcept; TextAttributeRun(const size_t cchLength, const TextAttribute attr) noexcept :
_cchLength(gsl::narrow<unsigned int>(cchLength))
{
SetAttributes(attr);
}
size_t GetLength() const noexcept; size_t GetLength() const noexcept { return _cchLength; }
void SetLength(const size_t cchLength) noexcept; void SetLength(const size_t cchLength) noexcept { _cchLength = gsl::narrow<unsigned int>(cchLength); }
void IncrementLength() noexcept; void IncrementLength() noexcept { _cchLength++; }
void DecrementLength() noexcept; void DecrementLength() noexcept { _cchLength--; }
const TextAttribute& GetAttributes() const noexcept; const TextAttribute& GetAttributes() const noexcept { return _attributes; }
void SetAttributes(const TextAttribute textAttribute) noexcept; void SetAttributes(const TextAttribute textAttribute) noexcept { _attributes = textAttribute; }
private: private:
size_t _cchLength; unsigned int _cchLength{ 0 };
TextAttribute _attributes; TextAttribute _attributes{ 0 };
#ifdef UNIT_TESTING #ifdef UNIT_TESTING
friend class AttrRowTests; friend class AttrRowTests;

View file

@ -6,7 +6,7 @@
<RootNamespace>bufferout</RootNamespace> <RootNamespace>bufferout</RootNamespace>
<ProjectName>BufferOut</ProjectName> <ProjectName>BufferOut</ProjectName>
<TargetName>ConBufferOut</TargetName> <TargetName>ConBufferOut</TargetName>
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
</PropertyGroup> </PropertyGroup>
<Import Project="$(SolutionDir)src\common.build.pre.props" /> <Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemGroup> <ItemGroup>
@ -22,7 +22,6 @@
<ClCompile Include="..\search.cpp" /> <ClCompile Include="..\search.cpp" />
<ClCompile Include="..\TextColor.cpp" /> <ClCompile Include="..\TextColor.cpp" />
<ClCompile Include="..\TextAttribute.cpp" /> <ClCompile Include="..\TextAttribute.cpp" />
<ClCompile Include="..\TextAttributeRun.cpp" />
<ClCompile Include="..\textBuffer.cpp" /> <ClCompile Include="..\textBuffer.cpp" />
<ClCompile Include="..\textBufferCellIterator.cpp" /> <ClCompile Include="..\textBufferCellIterator.cpp" />
<ClCompile Include="..\textBufferTextIterator.cpp" /> <ClCompile Include="..\textBufferTextIterator.cpp" />
@ -61,4 +60,4 @@
</ItemGroup> </ItemGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. --> <!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.post.props" /> <Import Project="$(SolutionDir)src\common.build.post.props" />
</Project> </Project>

View file

@ -40,7 +40,6 @@ SOURCES= \
..\RowCellIterator.cpp \ ..\RowCellIterator.cpp \
..\TextColor.cpp \ ..\TextColor.cpp \
..\TextAttribute.cpp \ ..\TextAttribute.cpp \
..\TextAttributeRun.cpp \
..\textBuffer.cpp \ ..\textBuffer.cpp \
..\textBufferCellIterator.cpp \ ..\textBufferCellIterator.cpp \
..\textBufferTextIterator.cpp \ ..\textBufferTextIterator.cpp \

View file

@ -42,6 +42,7 @@ TextBuffer::TextBuffer(const COORD screenBufferSize,
_currentPatternId{ 0 } _currentPatternId{ 0 }
{ {
// initialize ROWs // initialize ROWs
_storage.reserve(static_cast<size_t>(screenBufferSize.Y));
for (size_t i = 0; i < static_cast<size_t>(screenBufferSize.Y); ++i) for (size_t i = 0; i < static_cast<size_t>(screenBufferSize.Y); ++i)
{ {
_storage.emplace_back(static_cast<SHORT>(i), screenBufferSize.X, _currentAttributes, this); _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; const SHORT TopRowIndex = (GetFirstRowIndex() + TopRow) % currentSize.Y;
// rotate rows until the top row is at index 0 // rotate rows until the top row is at index 0
const ROW& newTopRow = _storage.at(TopRowIndex); for (int i = 0; i < TopRowIndex; i++)
while (&newTopRow != &_storage.front())
{ {
_storage.push_back(std::move(_storage.front())); _storage.emplace_back(std::move(_storage.front()));
_storage.pop_front(); _storage.erase(_storage.begin());
} }
_SetFirstRowIndex(0); _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 // all the text into one string and find the patterns in that string
for (auto i = firstRow; i <= lastRow; ++i) for (auto i = firstRow; i <= lastRow; ++i)
{ {
auto row = GetRowByOffset(i); auto& row = GetRowByOffset(i);
concatAll += row.GetCharRow().GetText(); concatAll += row.GetCharRow().GetText();
} }

View file

@ -49,6 +49,8 @@ filling in the last row, and updating the screen.
#pragma once #pragma once
#include <vector>
#include "cursor.h" #include "cursor.h"
#include "Row.hpp" #include "Row.hpp"
#include "TextAttribute.hpp" #include "TextAttribute.hpp"
@ -190,7 +192,7 @@ public:
private: private:
void _UpdateSize(); void _UpdateSize();
Microsoft::Console::Types::Viewport _size; Microsoft::Console::Types::Viewport _size;
std::deque<ROW> _storage; std::vector<ROW> _storage;
Cursor _cursor; Cursor _cursor;
SHORT _firstRow; // indexes top row (not necessarily 0) SHORT _firstRow; // indexes top row (not necessarily 0)

View file

@ -329,7 +329,7 @@ void Terminal::UpdateSettings(ICoreSettings settings)
{ {
try try
{ {
auto row = newTextBuffer->GetRowByOffset(::base::ClampSub(proposedTop, 1)); auto& row = newTextBuffer->GetRowByOffset(::base::ClampSub(proposedTop, 1));
if (row.GetCharRow().WasWrapForced()) if (row.GetCharRow().WasWrapForced())
{ {
proposedTop--; proposedTop--;

View file

@ -3529,7 +3529,7 @@ void ConptyRoundtripTests::HyperlinkIdConsistency()
auto verifyData = [](TextBuffer& tb) { auto verifyData = [](TextBuffer& tb) {
// Check that all the linked cells still have the same ID // 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(); auto id = attrRow.GetAttrByColumn(0).GetHyperlinkId();
for (auto i = 1; i < 4; ++i) for (auto i = 1; i < 4; ++i)
{ {

View file

@ -26,7 +26,7 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<ItemGroup Label="ProjectConfigurations"> <ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="AuditMode|Win32"> <ProjectConfiguration Include="AuditMode|Win32">
<Configuration>AuditMode</Configuration> <Configuration>AuditMode</Configuration>
<Platform>Win32</Platform> <Platform>Win32</Platform>
</ProjectConfiguration> </ProjectConfiguration>
@ -90,7 +90,7 @@
<PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>precomp.h</PrecompiledHeaderFile>
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <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> <MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild> <MinimalRebuild>false</MinimalRebuild>
<RuntimeTypeInfo>false</RuntimeTypeInfo> <RuntimeTypeInfo>false</RuntimeTypeInfo>

View file

@ -88,6 +88,8 @@ TRACELOGGING_DECLARE_PROVIDER(g_hConhostV2EventTraceProvider);
#include "../inc/operators.hpp" #include "../inc/operators.hpp"
#include "../inc/conattrs.hpp" #include "../inc/conattrs.hpp"
#include "boost/container/small_vector.hpp"
// TODO: MSFT 9355094 Find a better way of doing this. http://osgvsowi/9355094 // TODO: MSFT 9355094 Find a better way of doing this. http://osgvsowi/9355094
[[nodiscard]] inline NTSTATUS NTSTATUS_FROM_HRESULT(HRESULT hr) [[nodiscard]] inline NTSTATUS NTSTATUS_FROM_HRESULT(HRESULT hr)
{ {

View file

@ -235,6 +235,24 @@ class AttrRowTests
return NoThrowString().Format(L"%wc%d", run.GetAttributes().GetLegacyAttributes(), run.GetLength()); 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, void LogChain(_In_ PCWSTR pwszPrefix,
std::vector<TextAttributeRun>& chain) std::vector<TextAttributeRun>& chain)
{ {

View file

@ -80,19 +80,10 @@ public:
typedef struct _RegPropertyMap typedef struct _RegPropertyMap
{ {
_RegPropertyType const propertyType; _RegPropertyType propertyType;
PCWSTR pwszValueName; PCWSTR pwszValueName;
DWORD const dwFieldOffset; DWORD dwFieldOffset;
size_t const cbFieldSize; size_t cbFieldSize;
_RegPropertyMap(
_RegPropertyType const propertyType,
PCWSTR pwszValueName,
DWORD const dwFieldOffset,
size_t const cbFieldSize) :
propertyType(propertyType),
pwszValueName(pwszValueName),
dwFieldOffset(dwFieldOffset),
cbFieldSize(cbFieldSize){};
_RegPropertyMap& operator=(const _RegPropertyMap&) { return *this; } _RegPropertyMap& operator=(const _RegPropertyMap&) { return *this; }
} RegPropertyMap; } RegPropertyMap;