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 -?`.
### 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
// 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
{

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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))

View file

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

View file

@ -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();

View file

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

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
{
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;

View file

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

View file

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

View file

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

View file

@ -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)

View file

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

View file

@ -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)
{

View file

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

View file

@ -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)
{

View file

@ -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)
{

View file

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