diff --git a/.github/actions/spelling/excludes.txt b/.github/actions/spelling/excludes.txt
index 3f3c4a691d5..441d1f295ca 100644
--- a/.github/actions/spelling/excludes.txt
+++ b/.github/actions/spelling/excludes.txt
@@ -106,6 +106,7 @@
^src/terminal/parser/ft_fuzzwrapper/run\.bat$
^src/terminal/parser/ut_parser/Base64Test.cpp$
^src/terminal/parser/ut_parser/run\.bat$
+^src/tools/benchcat
^src/tools/integrity/packageuwp/ConsoleUWP\.appxSources$
^src/tools/lnkd/lnkd\.bat$
^src/tools/pixels/pixels\.bat$
diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt
index 7e7b46f7d3a..a0bc0ab0526 100644
--- a/.github/actions/spelling/expect/expect.txt
+++ b/.github/actions/spelling/expect/expect.txt
@@ -105,6 +105,7 @@ bcx
bcz
BEFOREPARENT
beginthread
+benchcat
bgfx
bgidx
Bgk
@@ -987,7 +988,6 @@ langid
LANGUAGELIST
lasterror
LASTEXITCODE
-lastexitcode
LAYOUTRTL
lbl
LBN
diff --git a/OpenConsole.sln b/OpenConsole.sln
index 571824e7250..c329b756ca4 100644
--- a/OpenConsole.sln
+++ b/OpenConsole.sln
@@ -420,6 +420,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TerminalStress", "src\tools
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RenderingTests", "src\tools\RenderingTests\RenderingTests.vcxproj", "{37C995E0-2349-4154-8E77-4A52C0C7F46D}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "benchcat", "src\tools\benchcat\benchcat.vcxproj", "{2C836962-9543-4CE5-B834-D28E1F124B66}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
AuditMode|Any CPU = AuditMode|Any CPU
@@ -2780,11 +2782,8 @@ Global
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|Any CPU.ActiveCfg = Debug|Win32
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|ARM.ActiveCfg = Debug|Win32
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|ARM64.ActiveCfg = Debug|ARM64
- {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|ARM64.Build.0 = Debug|ARM64
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|x64.ActiveCfg = Debug|x64
- {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|x64.Build.0 = Debug|x64
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|x86.ActiveCfg = Debug|Win32
- {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Debug|x86.Build.0 = Debug|Win32
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Fuzzing|ARM.ActiveCfg = Fuzzing|Win32
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64
@@ -2793,11 +2792,28 @@ Global
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|Any CPU.ActiveCfg = Release|Win32
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|ARM.ActiveCfg = Release|Win32
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|ARM64.ActiveCfg = Release|ARM64
- {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|ARM64.Build.0 = Release|ARM64
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x64.ActiveCfg = Release|x64
- {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x64.Build.0 = Release|x64
{37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x86.ActiveCfg = Release|Win32
- {37C995E0-2349-4154-8E77-4A52C0C7F46D}.Release|x86.Build.0 = Release|Win32
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|ARM.ActiveCfg = AuditMode|Win32
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|ARM64.ActiveCfg = Release|ARM64
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|x64.ActiveCfg = Release|x64
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.AuditMode|x86.ActiveCfg = Release|Win32
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|Any CPU.ActiveCfg = Debug|Win32
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|ARM.ActiveCfg = Debug|Win32
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|x64.ActiveCfg = Debug|x64
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Debug|x86.ActiveCfg = Debug|Win32
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|ARM.ActiveCfg = Fuzzing|Win32
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|x64.ActiveCfg = Fuzzing|x64
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Release|Any CPU.ActiveCfg = Release|Win32
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Release|ARM.ActiveCfg = Release|Win32
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Release|ARM64.ActiveCfg = Release|ARM64
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Release|x64.ActiveCfg = Release|x64
+ {2C836962-9543-4CE5-B834-D28E1F124B66}.Release|x86.ActiveCfg = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2904,6 +2920,7 @@ Global
{3C67784E-1453-49C2-9660-483E2CC7F7AD} = {40BD8415-DD93-4200-8D82-498DDDC08CC8}
{613CCB57-5FA9-48EF-80D0-6B1E319E20C4} = {A10C4720-DCA4-4640-9749-67F4314F527C}
{37C995E0-2349-4154-8E77-4A52C0C7F46D} = {A10C4720-DCA4-4640-9749-67F4314F527C}
+ {2C836962-9543-4CE5-B834-D28E1F124B66} = {A10C4720-DCA4-4640-9749-67F4314F527C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}
diff --git a/build/scripts/Invoke-FormattingCheck.ps1 b/build/scripts/Invoke-FormattingCheck.ps1
index ef780b5f480..d1110943d1f 100644
--- a/build/scripts/Invoke-FormattingCheck.ps1
+++ b/build/scripts/Invoke-FormattingCheck.ps1
@@ -10,7 +10,7 @@ function Invoke-CheckBadCodeFormatting() {
# returns a non-zero exit code if there are any diffs in the tracked files in the repo
git diff-index --quiet HEAD --
- if ($lastExitCode -eq 1) {
+ if ($LASTEXITCODE -eq 1) {
# Write the list of files that need updating to the log
git diff-index --name-only HEAD
diff --git a/src/tools/RenderingTests/RenderingTests.vcxproj b/src/tools/RenderingTests/RenderingTests.vcxproj
index 0824d3dd669..5de14c0f43e 100644
--- a/src/tools/RenderingTests/RenderingTests.vcxproj
+++ b/src/tools/RenderingTests/RenderingTests.vcxproj
@@ -4,6 +4,7 @@
16.0
Win32Proj
{37c995e0-2349-4154-8e77-4a52c0c7f46d}
+ RenderingTests
RenderingTests
10.0
diff --git a/src/tools/benchcat/benchcat.vcxproj b/src/tools/benchcat/benchcat.vcxproj
new file mode 100644
index 00000000000..67a51675a14
--- /dev/null
+++ b/src/tools/benchcat/benchcat.vcxproj
@@ -0,0 +1,45 @@
+
+
+
+ 16.0
+ Win32Proj
+ {2C836962-9543-4CE5-B834-D28E1F124B66}
+ benchcat
+ benchcat
+ 10.0
+ bc
+
+
+
+
+ NotUsing
+
+
+ Console
+
+
+
+ false
+ false
+
+
+
+ false
+ false
+ false
+ NODEFAULTLIB;%(PreprocessorDefinitions)
+ false
+
+
+ main
+ true
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tools/benchcat/crt.cpp b/src/tools/benchcat/crt.cpp
new file mode 100644
index 00000000000..f3dc3f6016e
--- /dev/null
+++ b/src/tools/benchcat/crt.cpp
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#ifdef NODEFAULTLIB
+
+#include
+
+#pragma function(memcpy)
+void* memcpy(void* dst, const void* src, size_t size)
+{
+ __movsb(static_cast(dst), static_cast(src), size);
+ return dst;
+}
+
+#pragma function(memset)
+void* memset(void* dst, int val, size_t size)
+{
+ __stosb(static_cast(dst), static_cast(val), size);
+ return dst;
+}
+
+#endif
diff --git a/src/tools/benchcat/main.cpp b/src/tools/benchcat/main.cpp
new file mode 100644
index 00000000000..ff2e63434be
--- /dev/null
+++ b/src/tools/benchcat/main.cpp
@@ -0,0 +1,692 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#define NOMINMAX
+#define WIN32_LEAN_AND_MEAN
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include "crt.cpp"
+
+// This warning is broken on if/else chains with init statements.
+#pragma warning(disable : 4456) // declaration of '...' hides previous local declaration
+
+namespace pcg_engines
+{
+ /*
+ * PCG Random Number Generation for C++
+ *
+ * Copyright 2014-2017 Melissa O'Neill ,
+ * and the PCG Project contributors.
+ *
+ * SPDX-License-Identifier: (Apache-2.0 OR MIT)
+ *
+ * Licensed under the Apache License, Version 2.0 (provided in
+ * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0)
+ * or under the MIT license (provided in LICENSE-MIT.txt and at
+ * http://opensource.org/licenses/MIT), at your option. This file may not
+ * be copied, modified, or distributed except according to those terms.
+ *
+ * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either
+ * express or implied. See your chosen license for details.
+ *
+ * For additional information about the PCG random number generation scheme,
+ * visit http://www.pcg-random.org/.
+ */
+
+ class oneseq_dxsm_64_32
+ {
+ using xtype = uint32_t;
+ using itype = uint64_t;
+
+ itype state_;
+
+ static constexpr uint64_t multiplier() noexcept
+ {
+ return 6364136223846793005ULL;
+ }
+
+ static constexpr uint64_t increment() noexcept
+ {
+ return 1442695040888963407ULL;
+ }
+
+ static constexpr itype bump(itype state) noexcept
+ {
+ return state * multiplier() + increment();
+ }
+
+ constexpr itype base_generate0() noexcept
+ {
+ itype old_state = state_;
+ state_ = bump(state_);
+ return old_state;
+ }
+
+ public:
+ explicit constexpr oneseq_dxsm_64_32(itype state = 0xcafef00dd15ea5e5ULL) noexcept :
+ state_(bump(state + increment()))
+ {
+ }
+
+ constexpr xtype operator()() noexcept
+ {
+ constexpr auto xtypebits = uint8_t(sizeof(xtype) * 8);
+ constexpr auto itypebits = uint8_t(sizeof(itype) * 8);
+ static_assert(xtypebits <= itypebits / 2, "Output type must be half the size of the state type.");
+
+ auto internal = base_generate0();
+ auto hi = xtype(internal >> (itypebits - xtypebits));
+ auto lo = xtype(internal);
+
+ lo |= 1;
+ hi ^= hi >> (xtypebits / 2);
+ hi *= xtype(multiplier());
+ hi ^= hi >> (3 * (xtypebits / 4));
+ hi *= lo;
+ return hi;
+ }
+
+ constexpr xtype operator()(xtype upper_bound) noexcept
+ {
+ uint32_t threshold = (UINT64_MAX + uint32_t(1) - upper_bound) % upper_bound;
+ for (;;)
+ {
+ auto r = operator()();
+ if (r >= threshold)
+ return r % upper_bound;
+ }
+ }
+ };
+} // namespace pcg_engines
+
+template
+constexpr T min(T a, T b)
+{
+ return a < b ? a : b;
+}
+
+template
+constexpr T max(T a, T b)
+{
+ return a > b ? a : b;
+}
+
+template
+constexpr T clamp(T val, T min, T max)
+{
+ return val < min ? min : (val > max ? max : val);
+}
+
+static uint32_t parse_number(const wchar_t* str, const wchar_t** end) noexcept
+{
+ static constexpr uint32_t max = 0x0fffffff;
+ uint32_t accumulator = 0;
+
+ for (;; ++str)
+ {
+ if (*str == '\0' || *str < '0' || *str > '9')
+ {
+ break;
+ }
+
+ accumulator = accumulator * 10 + *str - '0';
+ if (accumulator >= max)
+ {
+ accumulator = max;
+ break;
+ }
+ }
+
+ if (end)
+ {
+ *end = str;
+ }
+
+ return accumulator;
+}
+
+static uint32_t parse_number_with_suffix(const wchar_t* str) noexcept
+{
+ const wchar_t* str_end = nullptr;
+ auto value = parse_number(str, &str_end);
+
+ if (str_end[0])
+ {
+ uint32_t mul = 1000;
+
+ if (str_end[1])
+ {
+ mul = 1024;
+
+ if (str_end[1] != L'i')
+ {
+ value = 0;
+ }
+ }
+
+ switch (*str_end)
+ {
+ case L'g':
+ case L'G':
+ value *= mul;
+ [[fallthrough]];
+ case L'm':
+ case L'M':
+ value *= mul;
+ [[fallthrough]];
+ case L'k':
+ case L'K':
+ value *= mul;
+ break;
+ default:
+ value = 0;
+ break;
+ }
+ }
+
+ return value;
+}
+
+static bool has_suffix(const wchar_t* str, const wchar_t* suffix)
+{
+ return wcscmp(str, suffix) == 0;
+}
+
+static const wchar_t* split_prefix(const wchar_t* str, const wchar_t* prefix) noexcept
+{
+ for (; *prefix; ++prefix, ++str)
+ {
+ if (*str != *prefix)
+ {
+ return nullptr;
+ }
+ }
+ return str;
+}
+
+static char* buffer_append_long(char* dst, const void* src, size_t size)
+{
+ memcpy(dst, src, size);
+ return dst + size;
+}
+
+static char* buffer_append(char* dst, const char* src, size_t size)
+{
+ for (size_t i = 0; i < size; ++i, ++src, ++dst)
+ {
+ *dst = *src;
+ }
+ return dst;
+}
+
+static char* buffer_append_string(char* dst, const char* src)
+{
+ return buffer_append(dst, src, strlen(src));
+}
+
+char* buffer_append_number(char* dst, uint8_t val)
+{
+ if (val >= 10)
+ {
+ if (val >= 100)
+ {
+ const uint8_t d = val / 100;
+ *dst++ = '0' + d;
+ val -= d * 100;
+ }
+
+ const uint8_t d = val / 10;
+ *dst++ = '0' + d;
+ val -= d * 10;
+ }
+
+ *dst++ = '0' + val;
+ return dst;
+}
+
+struct FormatResult
+{
+ LONGLONG integral;
+ LONGLONG fractional;
+ const char* suffix;
+};
+
+#define FORMAT_RESULT_FMT "%lld.%03lld%s"
+#define FORMAT_RESULT_ARGS(r) r.integral, r.fractional, r.suffix
+
+static FormatResult format_size(LONGLONG value)
+{
+ FormatResult result;
+ if (value >= 1'000'000'000)
+ {
+ result.integral = value / 1'000'000'000;
+ result.fractional = ((value + 500'000) / 1'000'000) % 1000;
+ result.suffix = "G";
+ }
+ else if (value >= 1'000'000)
+ {
+ result.integral = value / 1'000'000;
+ result.fractional = ((value + 500) / 1'000) % 1000;
+ result.suffix = "M";
+ }
+ else if (value >= 1'000)
+ {
+ result.integral = value / 1'000;
+ result.fractional = value % 1'000;
+ result.suffix = "k";
+ }
+ else
+ {
+ result.integral = value;
+ result.fractional = 0;
+ result.suffix = "";
+ }
+ return result;
+}
+
+static FormatResult format_duration(LONGLONG microseconds)
+{
+ FormatResult result;
+ if (microseconds >= 1'000'000)
+ {
+ result.integral = microseconds / 1'000'000;
+ result.fractional = ((microseconds + 500) / 1'000) % 1000;
+ result.suffix = "";
+ }
+ else
+ {
+ result.integral = microseconds / 1'000;
+ result.fractional = microseconds % 1'000;
+ result.suffix = "m";
+ }
+ return result;
+}
+
+static int format(char* buffer, int size, const char* format, ...) noexcept
+{
+ va_list vl;
+ va_start(vl, format);
+ const auto length = wvnsprintfA(buffer, size, format, vl);
+ va_end(vl);
+ return length;
+}
+
+enum class VtMode
+{
+ Off,
+ On,
+ Italic,
+ Color
+};
+
+static HANDLE g_stdout;
+static HANDLE g_stderr;
+static UINT g_console_cp_old;
+static DWORD g_console_mode_old;
+static size_t g_large_page_minimum;
+
+[[noreturn]] static void clean_exit(UINT code)
+{
+ if (g_console_cp_old)
+ {
+ SetConsoleCP(g_console_cp_old);
+ }
+ if (g_console_mode_old)
+ {
+ SetConsoleMode(g_stdout, g_console_mode_old);
+ }
+ ExitProcess(code);
+}
+
+[[noreturn]] static void eprintf(const char* format, ...) noexcept
+{
+ char buffer[1024];
+
+ va_list vl;
+ va_start(vl, format);
+ const auto length = wvnsprintfA(buffer, sizeof(buffer), format, vl);
+ va_end(vl);
+
+ if (length > 0)
+ {
+ WriteFile(g_stderr, buffer, length, nullptr, nullptr);
+ }
+
+ clean_exit(1);
+}
+
+[[noreturn]] static void print_last_error(const char* what) noexcept
+{
+ eprintf("\r\nfailed to %s with 0x%08lx\r\n", what, GetLastError());
+}
+
+static void acquire_lock_memory_privilege() noexcept
+{
+ HANDLE token;
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
+ {
+ return;
+ }
+
+ TOKEN_PRIVILEGES privileges{};
+ privileges.PrivilegeCount = 1;
+ privileges.Privileges[0].Luid = { 4, 0 }; // SE_LOCK_MEMORY_PRIVILEGE is a well known LUID and always {4, 0}
+ privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+
+ // AdjustTokenPrivileges can return true and still set the last error to ERROR_NOT_ALL_ASSIGNED. This API is nuts...
+ const bool success = AdjustTokenPrivileges(token, FALSE, &privileges, 0, nullptr, nullptr);
+ if (success && GetLastError() == S_OK)
+ {
+ g_large_page_minimum = GetLargePageMinimum();
+ }
+
+ CloseHandle(token);
+}
+
+static char* allocate(size_t size)
+{
+ if (g_large_page_minimum)
+ {
+ const auto large_size = (size + g_large_page_minimum - 1) & ~(g_large_page_minimum - 1);
+ if (const auto address = static_cast(VirtualAlloc(nullptr, large_size, MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, PAGE_READWRITE)))
+ {
+ return address;
+ }
+ }
+
+ if (const auto address = static_cast(VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)))
+ {
+ return address;
+ }
+
+ print_last_error("allocate memory");
+}
+
+static BOOL WINAPI consoleCtrlHandler(DWORD)
+{
+ CancelIoEx(g_stdout, nullptr);
+ return TRUE;
+}
+
+int __stdcall main() noexcept
+{
+ g_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
+ g_stderr = GetStdHandle(STD_OUTPUT_HANDLE);
+ g_console_cp_old = GetConsoleOutputCP();
+
+ SetConsoleCtrlHandler(consoleCtrlHandler, TRUE);
+ SetConsoleOutputCP(CP_UTF8);
+
+ const wchar_t* path = nullptr;
+ uint32_t chunk_size = 128 * 1024;
+ uint32_t repeat = 1;
+ VtMode vt = VtMode::Off;
+ uint64_t seed = 0;
+ bool has_seed = false;
+
+ {
+ int argc;
+ const auto argv = CommandLineToArgvW(GetCommandLineW(), &argc);
+
+ for (int i = 1; i < argc; ++i)
+ {
+ if (const auto suffix = split_prefix(argv[i], L"-c"))
+ {
+ // 1GiB is the maximum buffer size WriteFile seems to accept.
+ chunk_size = min(parse_number_with_suffix(suffix), 1024 * 1024 * 1024);
+ }
+ else if (const auto suffix = split_prefix(argv[i], L"-r"))
+ {
+ repeat = parse_number_with_suffix(suffix);
+ }
+ else if (const auto suffix = split_prefix(argv[i], L"-v"))
+ {
+ vt = VtMode::On;
+ if (has_suffix(suffix, L"i"))
+ {
+ vt = VtMode::Italic;
+ }
+ else if (has_suffix(suffix, L"c"))
+ {
+ vt = VtMode::Color;
+ }
+ else if (*suffix)
+ {
+ break;
+ }
+ }
+ else if (has_suffix(argv[i], L"-s"))
+ {
+ seed = parse_number_with_suffix(suffix);
+ has_seed = true;
+ }
+ else
+ {
+ if (argc - i == 1)
+ {
+ path = argv[i];
+ }
+ break;
+ }
+ }
+ }
+
+ if (!path || !chunk_size || !repeat)
+ {
+ eprintf(
+ "bc [options] \r\n"
+ " -v enable VT\r\n"
+ " -vi print as italic\r\n"
+ " -vc print colorized\r\n"
+ " -c{d}{u} chunk size, defaults to 128Ki\r\n"
+ " -r{d}{u} repeats, defaults to 1\r\n"
+ " -s{d} RNG seed\r\n"
+ "{d} are base-10 digits\r\n"
+ "{u} are suffix units k, Ki, M, Mi, G, Gi\r\n");
+ }
+
+ if (!has_seed && vt == VtMode::Color)
+ {
+ const auto cryptbase = LoadLibraryExW(L"cryptbase.dll", nullptr, 0);
+ if (!cryptbase)
+ {
+ print_last_error("get handle to cryptbase.dll");
+ }
+
+ const auto RtlGenRandom = reinterpret_cast(GetProcAddress(cryptbase, "SystemFunction036"));
+ if (!RtlGenRandom)
+ {
+ print_last_error("get handle to RtlGenRandom");
+ }
+
+ RtlGenRandom(&seed, sizeof(seed));
+ }
+
+ pcg_engines::oneseq_dxsm_64_32 rng{ seed };
+
+ const auto stdout = GetStdHandle(STD_OUTPUT_HANDLE);
+ const auto file = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (file == INVALID_HANDLE_VALUE)
+ {
+ print_last_error("open file");
+ }
+
+ size_t file_size = 0;
+ {
+#ifdef _WIN64
+ LARGE_INTEGER i;
+ if (!GetFileSizeEx(file, &i))
+ {
+ print_last_error("open file");
+ }
+
+ file_size = static_cast(i.QuadPart);
+
+#else
+ file_size = GetFileSize(file, nullptr);
+ if (file_size == INVALID_FILE_SIZE)
+ {
+ print_last_error("open file");
+ }
+#endif
+ }
+
+ acquire_lock_memory_privilege();
+
+ const auto file_data = allocate(file_size);
+ auto stdout_size = file_size;
+ auto stdout_data = file_data;
+
+ {
+ auto read_data = file_data;
+ DWORD read = 0;
+
+ for (auto remaining = file_size; remaining > 0; remaining -= read, read_data += read)
+ {
+ read = static_cast(min(0xffffffff, remaining));
+ if (!ReadFile(file, read_data, read, &read, nullptr))
+ {
+ print_last_error("read");
+ }
+ }
+ }
+
+ switch (vt)
+ {
+ case VtMode::Italic:
+ {
+ stdout_data = allocate(file_size + 16);
+ auto p = stdout_data;
+ p = buffer_append_string(p, "\x1b[3m");
+ p = buffer_append_long(p, file_data, file_size);
+ p = buffer_append_string(p, "\x1b[0m");
+ stdout_size = static_cast(p - stdout_data);
+ break;
+ }
+ case VtMode::Color:
+ {
+ if (const auto icu = LoadLibraryExW(L"icuuc.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))
+ {
+ stdout_data = allocate(file_size * 20 + 8);
+ auto p = stdout_data;
+
+ const auto p_utext_openUTF8 = reinterpret_cast(GetProcAddress(icu, "utext_openUTF8"));
+ const auto p_ubrk_open = reinterpret_cast(GetProcAddress(icu, "ubrk_open"));
+ const auto p_ubrk_setUText = reinterpret_cast(GetProcAddress(icu, "ubrk_setUText"));
+ const auto p_ubrk_next = reinterpret_cast(GetProcAddress(icu, "ubrk_next"));
+
+ auto error = U_ZERO_ERROR;
+ UText text = UTEXT_INITIALIZER;
+ p_utext_openUTF8(&text, file_data, file_size, &error);
+
+ const auto it = p_ubrk_open(UBRK_CHARACTER, "", nullptr, 0, &error);
+ p_ubrk_setUText(it, &text, &error);
+
+ for (int32_t ubrk0 = 0, ubrk1; (ubrk1 = p_ubrk_next(it)) != UBRK_DONE; ubrk0 = ubrk1)
+ {
+ p = buffer_append_string(p, "\x1b[38;2");
+ for (int i = 0; i < 3; i++)
+ {
+ *p++ = ';';
+ p = buffer_append_number(p, static_cast(rng()));
+ }
+ p = buffer_append_string(p, "m");
+ p = buffer_append(p, file_data + ubrk0, ubrk1 - ubrk0);
+ }
+
+ p = buffer_append_string(p, "\x1b[39;49m");
+ stdout_size = static_cast(p - stdout_data);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ {
+ DWORD mode = 0;
+ if (!GetConsoleMode(g_stdout, &mode))
+ {
+ print_last_error("get console mode");
+ }
+
+ g_console_mode_old = mode;
+
+ mode |= ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
+ mode &= ~(ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN);
+ if (vt != VtMode::Off)
+ {
+ mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+ }
+
+ if (!SetConsoleMode(g_stdout, mode))
+ {
+ print_last_error("set console mode");
+ }
+ }
+
+ LARGE_INTEGER frequency, beg, end;
+ QueryPerformanceFrequency(&frequency);
+ QueryPerformanceCounter(&beg);
+
+ for (size_t iteration = 0; iteration < repeat; ++iteration)
+ {
+ auto write_data = stdout_data;
+ DWORD written = 0;
+
+ for (auto remaining = stdout_size; remaining != 0; remaining -= written, write_data += written)
+ {
+ written = static_cast(min(remaining, chunk_size));
+ if (!WriteFile(stdout, write_data, written, &written, nullptr))
+ {
+ print_last_error("write");
+ }
+ }
+ }
+
+ QueryPerformanceCounter(&end);
+
+ const auto elapsed_ticks = end.QuadPart - beg.QuadPart;
+ const auto elapsed_us = (elapsed_ticks * 1'000'000) / frequency.QuadPart;
+ LONGLONG total_size = static_cast(stdout_size) * repeat;
+ const auto bytes_per_second = (total_size * frequency.QuadPart) / elapsed_ticks;
+
+ const auto written = format_size(total_size);
+ const auto duration = format_duration(elapsed_us);
+ const auto throughput = format_size(bytes_per_second);
+
+ char status[128];
+ const auto status_length = format(
+ &status[0],
+ 1024,
+ FORMAT_RESULT_FMT "B, " FORMAT_RESULT_FMT "s, " FORMAT_RESULT_FMT "B/s",
+ FORMAT_RESULT_ARGS(written),
+ FORMAT_RESULT_ARGS(duration),
+ FORMAT_RESULT_ARGS(throughput));
+
+ if (status_length <= 0)
+ {
+ clean_exit(1);
+ }
+
+ char buffer[256];
+ char* buffer_end = &buffer[0];
+
+ buffer_end = buffer_append_string(buffer_end, "\r\n");
+ for (int i = 0; i < status_length; ++i)
+ {
+ *buffer_end++ = '-';
+ }
+ buffer_end = buffer_append_string(buffer_end, "\r\n");
+ buffer_end = buffer_append_long(buffer_end, &status[0], static_cast(status_length));
+ buffer_end = buffer_append_string(buffer_end, "\r\n");
+
+ WriteFile(g_stderr, &buffer[0], static_cast(buffer_end - &buffer[0]), nullptr, nullptr);
+ clean_exit(0);
+}
diff --git a/tools/OpenConsole.psm1 b/tools/OpenConsole.psm1
index fd0ededc112..3dd17957161 100644
--- a/tools/OpenConsole.psm1
+++ b/tools/OpenConsole.psm1
@@ -367,7 +367,7 @@ function Test-XamlFormat() {
$xamlsForStyler = (git ls-files "$root/**/*.xaml") -join ","
dotnet tool run xstyler -- -c "$root\XamlStyler.json" -f "$xamlsForStyler" --passive
- if ($lastExitCode -eq 1) {
+ if ($LASTEXITCODE -eq 1) {
throw "Xaml formatting bad, run Invoke-XamlFormat on branch"
}