mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-10 00:48:23 -06:00
wip
This commit is contained in:
parent
1daa180b22
commit
0e4f03c35a
@ -1035,8 +1035,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
if (windowId == 0)
|
||||
{
|
||||
// Try the name as an integer ID
|
||||
uint32_t temp;
|
||||
if (!Utils::StringToUint(window.c_str(), temp))
|
||||
const auto temp = til::parse_unsigned<uint32_t>(window.c_str());
|
||||
if (!temp)
|
||||
{
|
||||
TraceLoggingWrite(g_hRemotingProvider,
|
||||
"Monarch_MoveContent_FailedToParseId",
|
||||
@ -1045,7 +1045,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
windowId = temp;
|
||||
windowId = *temp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -19,39 +19,17 @@
|
||||
|
||||
using Microsoft::Console::Interactivity::ServiceLocator;
|
||||
|
||||
struct case_insensitive_hash
|
||||
struct insensitive_less
|
||||
{
|
||||
using is_transparent = void;
|
||||
using is_transparent = int;
|
||||
|
||||
std::size_t operator()(const std::wstring_view& key) const
|
||||
bool operator()(const std::wstring_view& lhs, const std::wstring_view& rhs) const noexcept
|
||||
{
|
||||
til::hasher h;
|
||||
for (const auto& ch : key)
|
||||
{
|
||||
h.write(::towlower(ch));
|
||||
}
|
||||
return h.finalize();
|
||||
return til::compare_ordinal_insensitive(lhs, rhs) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct case_insensitive_equality
|
||||
{
|
||||
using is_transparent = void;
|
||||
|
||||
bool operator()(const std::wstring_view& lhs, const std::wstring_view& rhs) const
|
||||
{
|
||||
return til::compare_ordinal_insensitive(lhs, rhs) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_map<std::wstring,
|
||||
std::unordered_map<std::wstring,
|
||||
std::wstring,
|
||||
case_insensitive_hash,
|
||||
case_insensitive_equality>,
|
||||
case_insensitive_hash,
|
||||
case_insensitive_equality>
|
||||
g_aliasData;
|
||||
static std::map<std::wstring, std::map<std::wstring, std::wstring, insensitive_less>, insensitive_less> g_aliasData;
|
||||
|
||||
// Routine Description:
|
||||
// - Adds a command line alias to the global set.
|
||||
@ -99,26 +77,26 @@ std::unordered_map<std::wstring,
|
||||
|
||||
try
|
||||
{
|
||||
std::wstring exeNameString(exeName);
|
||||
std::wstring sourceString(source);
|
||||
std::wstring targetString(target);
|
||||
|
||||
std::transform(exeNameString.begin(), exeNameString.end(), exeNameString.begin(), towlower);
|
||||
std::transform(sourceString.begin(), sourceString.end(), sourceString.begin(), towlower);
|
||||
|
||||
if (targetString.size() == 0)
|
||||
if (target.empty())
|
||||
{
|
||||
// Only try to dig in and erase if the exeName exists.
|
||||
auto exeData = g_aliasData.find(exeNameString);
|
||||
const auto exeData = g_aliasData.find(exeName);
|
||||
if (exeData != g_aliasData.end())
|
||||
{
|
||||
g_aliasData[exeNameString].erase(sourceString);
|
||||
// With C++23 this can be replaced with just a single erase() call.
|
||||
const auto sourceData = exeData->second.find(source);
|
||||
if (sourceData != exeData->second.end())
|
||||
{
|
||||
exeData->second.erase(sourceData);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Map will auto-create each level as necessary
|
||||
g_aliasData[exeNameString][sourceString] = targetString;
|
||||
// With C++26 this can written a lot shorter with operator[].
|
||||
const auto exeData = g_aliasData.emplace(std::piecewise_construct, std::forward_as_tuple(exeName), std::forward_as_tuple()).first;
|
||||
const auto sourceData = exeData->second.emplace(std::piecewise_construct, std::forward_as_tuple(source), std::forward_as_tuple()).first;
|
||||
sourceData->second.assign(exeName);
|
||||
}
|
||||
}
|
||||
CATCH_RETURN();
|
||||
@ -153,18 +131,18 @@ std::unordered_map<std::wstring,
|
||||
til::at(*target, 0) = UNICODE_NULL;
|
||||
}
|
||||
|
||||
std::wstring exeNameString(exeName);
|
||||
std::wstring sourceString(source);
|
||||
|
||||
// For compatibility, return ERROR_GEN_FAILURE for any result where the alias can't be found.
|
||||
// We use .find for the iterators then dereference to search without creating entries.
|
||||
const auto exeIter = g_aliasData.find(exeNameString);
|
||||
// In the past a not-found resulted in a STATUS_UNSUCCESSFUL.
|
||||
// This was then translated by ntdll into ERROR_GEN_FAILURE.
|
||||
// Since we stopped using NTSTATUS codes, we now return ERROR_GEN_FAILURE directly.
|
||||
const auto exeIter = g_aliasData.find(exeName);
|
||||
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_GEN_FAILURE), exeIter == g_aliasData.end());
|
||||
|
||||
const auto& exeData = exeIter->second;
|
||||
const auto sourceIter = exeData.find(sourceString);
|
||||
const auto sourceIter = exeData.find(source);
|
||||
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_GEN_FAILURE), sourceIter == exeData.end());
|
||||
|
||||
const auto& targetString = sourceIter->second;
|
||||
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_GEN_FAILURE), targetString.size() == 0);
|
||||
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_GEN_FAILURE), targetString.empty());
|
||||
|
||||
// TargetLength is a byte count, convert to characters.
|
||||
auto targetSize = targetString.size();
|
||||
@ -337,8 +315,7 @@ static std::wstring aliasesSeparator(L"=");
|
||||
auto exeIter = g_aliasData.find(exeNameString);
|
||||
if (exeIter != g_aliasData.end())
|
||||
{
|
||||
const auto& list = exeIter->second;
|
||||
for (auto& pair : list)
|
||||
for (auto& pair : exeIter->second)
|
||||
{
|
||||
// Alias stores lengths in bytes.
|
||||
auto cchSource = pair.first.size();
|
||||
@ -422,7 +399,7 @@ static std::wstring aliasesSeparator(L"=");
|
||||
void Alias::s_ClearCmdExeAliases()
|
||||
{
|
||||
// find without creating.
|
||||
auto exeIter = g_aliasData.find(L"cmd.exe");
|
||||
const auto exeIter = g_aliasData.find(L"cmd.exe");
|
||||
if (exeIter != g_aliasData.end())
|
||||
{
|
||||
exeIter->second.clear();
|
||||
@ -465,11 +442,10 @@ void Alias::s_ClearCmdExeAliases()
|
||||
const size_t cchNull = 1;
|
||||
|
||||
// Find without creating.
|
||||
auto exeIter = g_aliasData.find(exeNameString);
|
||||
const auto exeIter = g_aliasData.find(exeNameString);
|
||||
if (exeIter != g_aliasData.end())
|
||||
{
|
||||
const auto& list = exeIter->second;
|
||||
for (auto& pair : list)
|
||||
for (auto& pair : exeIter->second)
|
||||
{
|
||||
// Alias stores lengths in bytes.
|
||||
const auto cchSource = pair.first.size();
|
||||
|
||||
@ -757,38 +757,21 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont
|
||||
// GH#1222 and GH#9754 - Use the "virtual" viewport here, so that the
|
||||
// viewport snaps back to the virtual viewport's location.
|
||||
const auto currentViewport = buffer.GetVirtualViewport().ToInclusive();
|
||||
til::point delta;
|
||||
{
|
||||
// When evaluating the X offset, we must convert the buffer position to
|
||||
// equivalent screen coordinates, taking line rendition into account.
|
||||
const auto lineRendition = buffer.GetTextBuffer().GetLineRendition(position.y);
|
||||
const auto screenPosition = BufferToScreenLine(til::inclusive_rect{ position.x, position.y, position.x, position.y }, lineRendition);
|
||||
|
||||
if (currentViewport.left > screenPosition.left)
|
||||
{
|
||||
delta.x = screenPosition.left - currentViewport.left;
|
||||
}
|
||||
else if (currentViewport.right < screenPosition.right)
|
||||
{
|
||||
delta.x = screenPosition.right - currentViewport.right;
|
||||
}
|
||||
// When evaluating the X offset, we must convert the buffer position to
|
||||
// equivalent screen coordinates, taking line rendition into account.
|
||||
const auto lineRendition = buffer.GetTextBuffer().GetLineRendition(position.y);
|
||||
const auto screenPosition = BufferToScreenLine(til::inclusive_rect{ position.x, position.y, position.x, position.y }, lineRendition);
|
||||
|
||||
if (currentViewport.top > position.y)
|
||||
{
|
||||
delta.y = position.y - currentViewport.top;
|
||||
}
|
||||
else if (currentViewport.bottom < position.y)
|
||||
{
|
||||
delta.y = position.y - currentViewport.bottom;
|
||||
}
|
||||
}
|
||||
auto left = std::min(currentViewport.left, screenPosition.left);
|
||||
left = std::max(left, screenPosition.right - currentViewport.right + currentViewport.left);
|
||||
|
||||
auto top = std::min(currentViewport.top, screenPosition.top);
|
||||
top = std::max(top, screenPosition.bottom - currentViewport.bottom + currentViewport.top);
|
||||
|
||||
til::point newWindowOrigin;
|
||||
newWindowOrigin.x = currentViewport.left + delta.x;
|
||||
newWindowOrigin.y = currentViewport.top + delta.y;
|
||||
// SetViewportOrigin will worry about clamping these values to the
|
||||
// buffer for us.
|
||||
RETURN_IF_NTSTATUS_FAILED(buffer.SetViewportOrigin(true, newWindowOrigin, true));
|
||||
RETURN_IF_NTSTATUS_FAILED(buffer.SetViewportOrigin(true, { left, top }, true));
|
||||
|
||||
// SetViewportOrigin will only move the virtual bottom down, but in
|
||||
// this particular case we also need to allow the virtual bottom to
|
||||
|
||||
127
src/inc/til/point_span.h
Normal file
127
src/inc/til/point_span.h
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
// point_span can be pictured as a "selection" range inside our text buffer. So given
|
||||
// a text buffer of 10x4, a start of 4,1 and end of 7,3 the span might look like this:
|
||||
// +----------+
|
||||
// | |
|
||||
// | xxxxxx|
|
||||
// |xxxxxxxxxx|
|
||||
// |xxxxxxxx |
|
||||
// +----------+
|
||||
// At the time of writing there's a push to make selections have an exclusive end coordinate,
|
||||
// so the interpretation of end might change soon (making this comment potentially outdated).
|
||||
struct point_span
|
||||
{
|
||||
til::point start;
|
||||
til::point end;
|
||||
|
||||
constexpr bool operator==(const point_span& rhs) const noexcept
|
||||
{
|
||||
// `__builtin_memcmp` isn't an official standard, but it's the
|
||||
// only way at the time of writing to get a constexpr `memcmp`.
|
||||
return __builtin_memcmp(this, &rhs, sizeof(rhs)) == 0;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const point_span& rhs) const noexcept
|
||||
{
|
||||
return __builtin_memcmp(this, &rhs, sizeof(rhs)) != 0;
|
||||
}
|
||||
|
||||
// Calls func(row, begX, endX) for each row and begX and begY are inclusive coordinates,
|
||||
// because point_span itself also uses inclusive coordinates.
|
||||
// In other words, it turns a
|
||||
// ```
|
||||
// +----------------+
|
||||
// | #########|
|
||||
// |################|
|
||||
// |#### |
|
||||
// +----------------+
|
||||
// ```
|
||||
// into:
|
||||
// ```
|
||||
// func(0, 8, 15)
|
||||
// func(1, 0, 15)
|
||||
// func(2, 0, 4)
|
||||
// ```
|
||||
constexpr void iterate_rows(til::CoordType width, auto&& func) const
|
||||
{
|
||||
// Copy the members so that the compiler knows it doesn't
|
||||
// need to re-read them on every loop iteration.
|
||||
const auto w = width - 1;
|
||||
const auto ax = std::clamp(start.x, 0, w);
|
||||
const auto ay = start.y;
|
||||
const auto bx = std::clamp(end.x, 0, w);
|
||||
const auto by = end.y;
|
||||
|
||||
for (auto y = ay; y <= by; ++y)
|
||||
{
|
||||
const auto x1 = y != ay ? 0 : ax;
|
||||
const auto x2 = y != by ? w : bx;
|
||||
func(y, x1, x2);
|
||||
}
|
||||
}
|
||||
|
||||
til::small_vector<til::inclusive_rect, 3> split_rects(til::CoordType width) const
|
||||
{
|
||||
// Copy the members so that the compiler knows it doesn't
|
||||
// need to re-read them on every loop iteration.
|
||||
const auto w = width - 1;
|
||||
const auto ax = std::clamp(start.x, 0, w);
|
||||
const auto ay = start.y;
|
||||
const auto bx = std::clamp(end.x, 0, w);
|
||||
const auto by = end.y;
|
||||
auto y = ay;
|
||||
|
||||
til::small_vector<til::inclusive_rect, 3> rects;
|
||||
|
||||
// +----------------+
|
||||
// | #########| <-- A
|
||||
// |################| <-- B
|
||||
// |#### | <-- C
|
||||
// +----------------+
|
||||
//
|
||||
// +----------------+
|
||||
// |################| <-- B
|
||||
// |################| <-- B
|
||||
// |#### | <-- C
|
||||
// +----------------+
|
||||
//
|
||||
// +----------------+
|
||||
// | #########| <-- A
|
||||
// |################| <-- C
|
||||
// |################| <-- C
|
||||
// +----------------+
|
||||
//
|
||||
// +----------------+
|
||||
// |################| <-- C
|
||||
// |################| <-- C
|
||||
// |################| <-- C
|
||||
// +----------------+
|
||||
|
||||
if (y <= by && ax > 0) // A
|
||||
{
|
||||
const auto x2 = y == by ? bx : w;
|
||||
rects.emplace_back(ax, y, x2, y);
|
||||
y += 1;
|
||||
}
|
||||
|
||||
if (y < by && bx < w) // B
|
||||
{
|
||||
rects.emplace_back(0, y, w, by - 1);
|
||||
y = by - 1;
|
||||
}
|
||||
|
||||
if (y <= by) // C
|
||||
{
|
||||
rects.emplace_back(0, y, bx, by);
|
||||
}
|
||||
|
||||
return rects;
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -3448,36 +3448,42 @@ void AdaptDispatch::DoConEmuAction(const std::wstring_view string)
|
||||
constexpr size_t TaskbarMaxState{ 4 };
|
||||
constexpr size_t TaskbarMaxProgress{ 100 };
|
||||
|
||||
unsigned int state = 0;
|
||||
unsigned int progress = 0;
|
||||
|
||||
const auto parts = Utils::SplitString(string, L';');
|
||||
unsigned int subParam = 0;
|
||||
if (parts.size() < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (parts.size() < 1 || !Utils::StringToUint(til::at(parts, 0), subParam))
|
||||
const auto subParam = til::parse_unsigned<unsigned int>(til::at(parts, 0));
|
||||
if (!subParam)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 4 is SetProgressBar, which sets the taskbar state/progress.
|
||||
if (subParam == 4)
|
||||
if (*subParam == 4)
|
||||
{
|
||||
if (parts.size() >= 2)
|
||||
unsigned int state = 0;
|
||||
if (parts.size() >= 2 && !til::at(parts, 1).empty())
|
||||
{
|
||||
// A state parameter is defined, parse it out
|
||||
const auto stateSuccess = Utils::StringToUint(til::at(parts, 1), state);
|
||||
if (!stateSuccess && !til::at(parts, 1).empty())
|
||||
const auto stateOpt = til::parse_unsigned<unsigned int>(til::at(parts, 1));
|
||||
state = stateOpt.value_or(0);
|
||||
if (!stateOpt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (parts.size() >= 3)
|
||||
}
|
||||
|
||||
unsigned int progress = 0;
|
||||
if (parts.size() >= 3 && !til::at(parts, 2).empty())
|
||||
{
|
||||
// A progress parameter is also defined, parse it out
|
||||
const auto progressOpt = til::parse_unsigned<unsigned int>(til::at(parts, 2));
|
||||
progress = progressOpt.value_or(0);
|
||||
if (!progressOpt)
|
||||
{
|
||||
// A progress parameter is also defined, parse it out
|
||||
const auto progressSuccess = Utils::StringToUint(til::at(parts, 2), progress);
|
||||
if (!progressSuccess && !til::at(parts, 2).empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3494,7 +3500,7 @@ void AdaptDispatch::DoConEmuAction(const std::wstring_view string)
|
||||
_api.SetTaskbarProgress(static_cast<DispatchTypes::TaskbarState>(state), progress);
|
||||
}
|
||||
// 9 is SetWorkingDirectory, which informs the terminal about the current working directory.
|
||||
else if (subParam == 9)
|
||||
else if (*subParam == 9)
|
||||
{
|
||||
if (parts.size() >= 2)
|
||||
{
|
||||
@ -3523,7 +3529,7 @@ void AdaptDispatch::DoConEmuAction(const std::wstring_view string)
|
||||
// * https://conemu.github.io/en/ShellWorkDir.html#PowerShell
|
||||
//
|
||||
// This seems like basically the same as 133;B - the end of the prompt, the start of the commandline.
|
||||
else if (subParam == 12)
|
||||
else if (*subParam == 12)
|
||||
{
|
||||
_pages.ActivePage().Buffer().StartCommand();
|
||||
}
|
||||
@ -3613,9 +3619,7 @@ void AdaptDispatch::DoFinalTermAction(const std::wstring_view string)
|
||||
// error and move on.
|
||||
//
|
||||
// We know that "0" will be successfully parsed, and that's close enough.
|
||||
unsigned int parsedError = 0;
|
||||
error = Utils::StringToUint(errorString, parsedError) ? parsedError :
|
||||
UINT_MAX;
|
||||
error = til::parse_unsigned<unsigned int>(errorString).value_or(UINT_MAX);
|
||||
}
|
||||
|
||||
_pages.ActivePage().Buffer().EndCurrentCommand(error);
|
||||
@ -3667,16 +3671,26 @@ void AdaptDispatch::DoVsCodeAction(const std::wstring_view string)
|
||||
// 2: $($completions.ReplacementLength);
|
||||
// 3: $($cursorIndex);
|
||||
// 4: $completions.CompletionMatches | ConvertTo-Json
|
||||
unsigned int replacementIndex = 0;
|
||||
unsigned int replacementLength = 0;
|
||||
unsigned int cursorIndex = 0;
|
||||
std::optional<unsigned int> replacementIndex;
|
||||
std::optional<unsigned int> replacementLength;
|
||||
std::optional<unsigned int> cursorIndex;
|
||||
bool succeeded = true;
|
||||
|
||||
bool succeeded = (parts.size() >= 2) &&
|
||||
(Utils::StringToUint(til::at(parts, 1), replacementIndex));
|
||||
succeeded &= (parts.size() >= 3) &&
|
||||
(Utils::StringToUint(til::at(parts, 2), replacementLength));
|
||||
succeeded &= (parts.size() >= 4) &&
|
||||
(Utils::StringToUint(til::at(parts, 3), cursorIndex));
|
||||
if (parts.size() >= 2)
|
||||
{
|
||||
replacementIndex = til::parse_unsigned<unsigned int>(til::at(parts, 1));
|
||||
succeeded &= replacementIndex.has_value();
|
||||
}
|
||||
if (parts.size() >= 3)
|
||||
{
|
||||
replacementLength = til::parse_unsigned<unsigned int>(til::at(parts, 2));
|
||||
succeeded &= replacementLength.has_value();
|
||||
}
|
||||
if (parts.size() >= 4)
|
||||
{
|
||||
cursorIndex = til::parse_unsigned<unsigned int>(til::at(parts, 3));
|
||||
succeeded &= cursorIndex.has_value();
|
||||
}
|
||||
|
||||
// VsCode is using cursorIndex and replacementIndex, but we aren't currently.
|
||||
if (succeeded)
|
||||
@ -3695,7 +3709,7 @@ void AdaptDispatch::DoVsCodeAction(const std::wstring_view string)
|
||||
const auto remainder = string.substr(prefixLength);
|
||||
|
||||
_api.InvokeCompletions(parts.size() < 5 ? L"" : remainder,
|
||||
replacementLength);
|
||||
replacementLength.value_or(0));
|
||||
}
|
||||
|
||||
// If it's poorly formatted, just eat it
|
||||
|
||||
@ -909,19 +909,18 @@ bool OutputStateMachineEngine::_GetOscSetColorTable(const std::wstring_view stri
|
||||
{
|
||||
auto&& index = til::at(parts, i);
|
||||
auto&& color = til::at(parts, j);
|
||||
unsigned int tableIndex = 0;
|
||||
const auto indexSuccess = Utils::StringToUint(index, tableIndex);
|
||||
const auto tableIndex = til::parse_unsigned<unsigned int>(index);
|
||||
|
||||
if (indexSuccess)
|
||||
if (tableIndex)
|
||||
{
|
||||
if (color == L"?"sv) [[unlikely]]
|
||||
{
|
||||
newTableIndexes.push_back(tableIndex);
|
||||
newTableIndexes.push_back(*tableIndex);
|
||||
newRgbs.push_back(COLOR_INQUIRY_COLOR);
|
||||
}
|
||||
else if (const auto colorOptional = Utils::ColorFromXTermColor(color))
|
||||
{
|
||||
newTableIndexes.push_back(tableIndex);
|
||||
newTableIndexes.push_back(*tableIndex);
|
||||
newRgbs.push_back(colorOptional.value());
|
||||
}
|
||||
}
|
||||
|
||||
@ -417,7 +417,7 @@ static BOOL WINAPI consoleCtrlHandler(DWORD)
|
||||
int __stdcall main() noexcept
|
||||
{
|
||||
g_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
g_stderr = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
g_stderr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
g_console_cp_old = GetConsoleOutputCP();
|
||||
|
||||
SetConsoleCtrlHandler(consoleCtrlHandler, TRUE);
|
||||
|
||||
@ -289,11 +289,12 @@ constexpr void Utils::InitializeExtendedColorTable(const std::span<COLORREF> tab
|
||||
// Return Value:
|
||||
// - An optional color which contains value if a color was successfully parsed
|
||||
std::optional<til::color> Utils::ColorFromXOrgAppColorName(const std::wstring_view wstr) noexcept
|
||||
try
|
||||
{
|
||||
std::string stem;
|
||||
char stemBuffer[32];
|
||||
size_t stemLength = 0;
|
||||
size_t variantIndex = 0;
|
||||
auto foundVariant = false;
|
||||
|
||||
for (const auto c : wstr)
|
||||
{
|
||||
// X11 guarantees that characters are all Latin1.
|
||||
@ -317,7 +318,7 @@ try
|
||||
continue;
|
||||
}
|
||||
|
||||
if (foundVariant)
|
||||
if (foundVariant || stemLength >= std::size(stemBuffer))
|
||||
{
|
||||
// Variant should be at the end of the string, e.g., "yellow3".
|
||||
// This means another non-numeric character is seen, e.g., "yellow3a".
|
||||
@ -325,9 +326,10 @@ try
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
stem += gsl::narrow_cast<char>(til::tolower_ascii(c));
|
||||
stemBuffer[stemLength++] = gsl::narrow_cast<char>(til::tolower_ascii(c));
|
||||
}
|
||||
|
||||
const std::string_view stem{ &stemBuffer[0], stemLength };
|
||||
const auto variantColorIter = xorgAppVariantColorTable.find(stem);
|
||||
if (variantColorIter != xorgAppVariantColorTable.end())
|
||||
{
|
||||
@ -345,7 +347,7 @@ try
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
const auto component{ ::base::saturated_cast<uint8_t>(((variantIndex * 255) + 50) / 100) };
|
||||
const auto component{ gsl::narrow_cast<uint8_t>(((variantIndex * 255) + 50) / 100) };
|
||||
return til::color{ component, component, component };
|
||||
}
|
||||
|
||||
@ -357,10 +359,5 @@ try
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
@ -64,9 +64,7 @@ namespace Microsoft::Console::Utils
|
||||
til::color ColorFromRGB100(const int r, const int g, const int b) noexcept;
|
||||
std::tuple<int, int, int> ColorToRGB100(const til::color color) noexcept;
|
||||
|
||||
bool HexToUint(const wchar_t wch, unsigned int& value) noexcept;
|
||||
bool StringToUint(const std::wstring_view wstr, unsigned int& value);
|
||||
std::vector<std::wstring_view> SplitString(const std::wstring_view wstr, const wchar_t delimiter) noexcept;
|
||||
til::small_vector<std::wstring_view, 4> SplitString(const std::wstring_view wstr, const wchar_t delimiter) noexcept;
|
||||
|
||||
enum FilterOption
|
||||
{
|
||||
@ -81,33 +79,6 @@ namespace Microsoft::Console::Utils
|
||||
|
||||
std::wstring FilterStringForPaste(const std::wstring_view wstr, const FilterOption option);
|
||||
|
||||
constexpr uint16_t EndianSwap(uint16_t value)
|
||||
{
|
||||
return (value & 0xFF00) >> 8 |
|
||||
(value & 0x00FF) << 8;
|
||||
}
|
||||
|
||||
constexpr uint32_t EndianSwap(uint32_t value)
|
||||
{
|
||||
return (value & 0xFF000000) >> 24 |
|
||||
(value & 0x00FF0000) >> 8 |
|
||||
(value & 0x0000FF00) << 8 |
|
||||
(value & 0x000000FF) << 24;
|
||||
}
|
||||
|
||||
constexpr unsigned long EndianSwap(unsigned long value)
|
||||
{
|
||||
return gsl::narrow_cast<unsigned long>(EndianSwap(gsl::narrow_cast<uint32_t>(value)));
|
||||
}
|
||||
|
||||
constexpr GUID EndianSwap(GUID value)
|
||||
{
|
||||
value.Data1 = EndianSwap(value.Data1);
|
||||
value.Data2 = EndianSwap(value.Data2);
|
||||
value.Data3 = EndianSwap(value.Data3);
|
||||
return value;
|
||||
}
|
||||
|
||||
GUID CreateV5Uuid(const GUID& namespaceGuid, const std::span<const std::byte> name);
|
||||
|
||||
bool CanUwpDragDrop();
|
||||
|
||||
@ -3,11 +3,8 @@
|
||||
|
||||
#include "precomp.h"
|
||||
#include "WexTestClass.h"
|
||||
#include "../../inc/consoletaeftemplates.hpp"
|
||||
|
||||
#include "../inc/utils.hpp"
|
||||
#include "../inc/colorTable.hpp"
|
||||
#include <conattrs.hpp>
|
||||
|
||||
using namespace WEX::Common;
|
||||
using namespace WEX::Logging;
|
||||
@ -23,7 +20,7 @@ class UtilsTests
|
||||
TEST_METHOD(TestGuidToString);
|
||||
TEST_METHOD(TestSplitString);
|
||||
TEST_METHOD(TestFilterStringForPaste);
|
||||
TEST_METHOD(TestStringToUint);
|
||||
TEST_METHOD(TestColorFromHexString);
|
||||
TEST_METHOD(TestColorFromXTermColor);
|
||||
|
||||
#if !__INSIDE_WINDOWS
|
||||
@ -87,7 +84,7 @@ void UtilsTests::TestGuidToString()
|
||||
|
||||
void UtilsTests::TestSplitString()
|
||||
{
|
||||
std::vector<std::wstring_view> result;
|
||||
til::small_vector<std::wstring_view, 4> result;
|
||||
result = SplitString(L"", L';');
|
||||
VERIFY_ARE_EQUAL(0u, result.size());
|
||||
result = SplitString(L"1", L';');
|
||||
@ -201,28 +198,11 @@ void UtilsTests::TestFilterStringForPaste()
|
||||
FilterStringForPaste(unicodeString, FilterOption::CarriageReturnNewline | FilterOption::ControlCodes));
|
||||
}
|
||||
|
||||
void UtilsTests::TestStringToUint()
|
||||
void UtilsTests::TestColorFromHexString()
|
||||
{
|
||||
auto success = false;
|
||||
unsigned int value = 0;
|
||||
success = StringToUint(L"", value);
|
||||
VERIFY_IS_FALSE(success);
|
||||
success = StringToUint(L"xyz", value);
|
||||
VERIFY_IS_FALSE(success);
|
||||
success = StringToUint(L";", value);
|
||||
VERIFY_IS_FALSE(success);
|
||||
|
||||
success = StringToUint(L"1", value);
|
||||
VERIFY_IS_TRUE(success);
|
||||
VERIFY_ARE_EQUAL(1u, value);
|
||||
|
||||
success = StringToUint(L"123", value);
|
||||
VERIFY_IS_TRUE(success);
|
||||
VERIFY_ARE_EQUAL(123u, value);
|
||||
|
||||
success = StringToUint(L"123456789", value);
|
||||
VERIFY_IS_TRUE(success);
|
||||
VERIFY_ARE_EQUAL(123456789u, value);
|
||||
VERIFY_ARE_EQUAL(til::color(0xAA, 0xBB, 0xCC), ColorFromHexString("#abc"));
|
||||
VERIFY_ARE_EQUAL(til::color(0xAB, 0xCD, 0xEF), ColorFromHexString("#abcdef"));
|
||||
VERIFY_ARE_EQUAL(til::color(0xAB, 0xCD, 0xEF, 0x01), ColorFromHexString("#abcdef01"));
|
||||
}
|
||||
|
||||
void UtilsTests::TestColorFromXTermColor()
|
||||
|
||||
@ -11,17 +11,6 @@
|
||||
|
||||
using namespace Microsoft::Console;
|
||||
|
||||
// Routine Description:
|
||||
// - Determines if a character is a valid number character, 0-9.
|
||||
// Arguments:
|
||||
// - wch - Character to check.
|
||||
// Return Value:
|
||||
// - True if it is. False if it isn't.
|
||||
static constexpr bool _isNumber(const wchar_t wch) noexcept
|
||||
{
|
||||
return wch >= L'0' && wch <= L'9'; // 0x30 - 0x39
|
||||
}
|
||||
|
||||
GSL_SUPPRESS(bounds)
|
||||
static std::wstring guidToStringCommon(const GUID& guid, size_t offset, size_t length)
|
||||
{
|
||||
@ -103,43 +92,34 @@ std::string Utils::ColorToHexString(const til::color color)
|
||||
// the correct format, throws E_INVALIDARG
|
||||
til::color Utils::ColorFromHexString(const std::string_view str)
|
||||
{
|
||||
THROW_HR_IF(E_INVALIDARG, str.size() != 9 && str.size() != 7 && str.size() != 4);
|
||||
THROW_HR_IF(E_INVALIDARG, str.at(0) != '#');
|
||||
THROW_HR_IF(E_INVALIDARG, str.empty() || str.at(0) != '#');
|
||||
|
||||
std::string rStr;
|
||||
std::string gStr;
|
||||
std::string bStr;
|
||||
std::string aStr;
|
||||
wchar_t wch[8];
|
||||
wch[6] = wch[7] = L'f';
|
||||
|
||||
if (str.size() == 4)
|
||||
switch (str.size())
|
||||
{
|
||||
rStr = std::string(2, str.at(1));
|
||||
gStr = std::string(2, str.at(2));
|
||||
bStr = std::string(2, str.at(3));
|
||||
aStr = "ff";
|
||||
}
|
||||
else if (str.size() == 7)
|
||||
{
|
||||
rStr = std::string(&str.at(1), 2);
|
||||
gStr = std::string(&str.at(3), 2);
|
||||
bStr = std::string(&str.at(5), 2);
|
||||
aStr = "ff";
|
||||
}
|
||||
else if (str.size() == 9)
|
||||
{
|
||||
// #rrggbbaa
|
||||
rStr = std::string(&str.at(1), 2);
|
||||
gStr = std::string(&str.at(3), 2);
|
||||
bStr = std::string(&str.at(5), 2);
|
||||
aStr = std::string(&str.at(7), 2);
|
||||
case 4:
|
||||
// For RGB we need to widen it to RRGGBB.
|
||||
wch[0] = wch[1] = str.at(1);
|
||||
wch[2] = wch[3] = str.at(2);
|
||||
wch[4] = wch[5] = str.at(3);
|
||||
break;
|
||||
case 7:
|
||||
case 9:
|
||||
// For RRGGBB or RRGGBBAA we can just copy it directly.
|
||||
std::copy(str.begin() + 1, str.end(), &wch[0]);
|
||||
break;
|
||||
default:
|
||||
THROW_HR(E_INVALIDARG);
|
||||
}
|
||||
|
||||
const auto r = gsl::narrow_cast<BYTE>(std::stoul(rStr, nullptr, 16));
|
||||
const auto g = gsl::narrow_cast<BYTE>(std::stoul(gStr, nullptr, 16));
|
||||
const auto b = gsl::narrow_cast<BYTE>(std::stoul(bStr, nullptr, 16));
|
||||
const auto a = gsl::narrow_cast<BYTE>(std::stoul(aStr, nullptr, 16));
|
||||
const auto rgba = til::parse_unsigned<uint32_t>({ &wch[0], 8 }, 16);
|
||||
THROW_HR_IF(E_INVALIDARG, !rgba);
|
||||
|
||||
return til::color{ r, g, b, a };
|
||||
til::color c;
|
||||
c.abgr = _byteswap_ulong(*rgba);
|
||||
return c;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@ -175,175 +155,103 @@ std::optional<til::color> Utils::ColorFromXTermColor(const std::wstring_view str
|
||||
// - string - The string containing the color spec string to parse.
|
||||
// Return Value:
|
||||
// - An optional color which contains value if a color was successfully parsed
|
||||
std::optional<til::color> Utils::ColorFromXParseColorSpec(const std::wstring_view string) noexcept
|
||||
try
|
||||
std::optional<til::color> Utils::ColorFromXParseColorSpec(std::wstring_view string) noexcept
|
||||
{
|
||||
auto foundXParseColorSpec = false;
|
||||
auto foundValidColorSpec = false;
|
||||
|
||||
auto isSharpSignFormat = false;
|
||||
size_t rgbHexDigitCount = 0;
|
||||
std::array<unsigned int, 3> colorValues = { 0 };
|
||||
std::array<unsigned int, 3> parameterValues = { 0 };
|
||||
const auto stringSize = string.size();
|
||||
unsigned int parameters[3]{};
|
||||
|
||||
// First we look for "rgb:"
|
||||
// Other colorspaces are theoretically possible, but we don't support them.
|
||||
auto curr = string.cbegin();
|
||||
if (stringSize > 4)
|
||||
if (til::starts_with_insensitive_ascii(string, L"rgb:"))
|
||||
{
|
||||
auto prefix = std::wstring(string.substr(0, 4));
|
||||
|
||||
// The "rgb:" indicator should be case insensitive. To prevent possible issues under
|
||||
// different locales, transform only ASCII range latin characters.
|
||||
std::transform(prefix.begin(), prefix.end(), prefix.begin(), [](const auto x) {
|
||||
return x >= L'A' && x <= L'Z' ? static_cast<wchar_t>(std::towlower(x)) : x;
|
||||
});
|
||||
|
||||
if (prefix.compare(L"rgb:") == 0)
|
||||
// If all the components have the same digit count, we can have one of the following formats:
|
||||
// 9 "rgb:h/h/h"
|
||||
// 12 "rgb:hh/hh/hh"
|
||||
// 15 "rgb:hhh/hhh/hhh"
|
||||
// 18 "rgb:hhhh/hhhh/hhhh"
|
||||
// Note that the component sizes aren't required to be the same.
|
||||
// Anything in between is also valid, e.g. "rgb:h/hh/h" and "rgb:h/hh/hhh".
|
||||
// Any fewer cannot be valid, and any more will be too many. Return early in this case.
|
||||
if (stringSize < 9 || stringSize > 18)
|
||||
{
|
||||
// If all the components have the same digit count, we can have one of the following formats:
|
||||
// 9 "rgb:h/h/h"
|
||||
// 12 "rgb:hh/hh/hh"
|
||||
// 15 "rgb:hhh/hhh/hhh"
|
||||
// 18 "rgb:hhhh/hhhh/hhhh"
|
||||
// Note that the component sizes aren't required to be the same.
|
||||
// Anything in between is also valid, e.g. "rgb:h/hh/h" and "rgb:h/hh/hhh".
|
||||
// Any fewer cannot be valid, and any more will be too many. Return early in this case.
|
||||
if (stringSize < 9 || stringSize > 18)
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
const auto remaining = string.substr(4);
|
||||
|
||||
for (auto&& part : til::split_iterator{ remaining, L'/' })
|
||||
{
|
||||
if (i >= std::size(parameters) || part.size() < 1 || part.size() > 4)
|
||||
{
|
||||
return std::nullopt;
|
||||
return {};
|
||||
}
|
||||
|
||||
foundXParseColorSpec = true;
|
||||
const auto val = til::parse_unsigned<unsigned int>(part, 16);
|
||||
if (!val)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::advance(curr, 4);
|
||||
auto v = *val;
|
||||
|
||||
// Map `v` from its 4/8/12/16-bit range to 8-bit.
|
||||
const auto bits = static_cast<unsigned int>(part.size() * 4);
|
||||
// `div` will be 0xf/0xff/0xfff/0xffff respectively.
|
||||
const auto div = (1ul << bits) - 1;
|
||||
// Adding `div / 2` will approximately round the value.
|
||||
v = (v * 255 + div / 2) / div;
|
||||
|
||||
parameters[i] = v;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Try the sharp sign format.
|
||||
if (!foundXParseColorSpec && stringSize > 1)
|
||||
else if (string.starts_with(L'#'))
|
||||
{
|
||||
if (til::at(string, 0) == L'#')
|
||||
// We can have one of the following formats:
|
||||
// 4 "#hhh"
|
||||
// 7 "#hhhhhh"
|
||||
// 10 "#hhhhhhhhh"
|
||||
// 13 "#hhhhhhhhhhhh"
|
||||
// Any other cases will be invalid. Return early in this case.
|
||||
if (stringSize != 4 && stringSize != 7 && stringSize != 10 && stringSize != 13)
|
||||
{
|
||||
// We can have one of the following formats:
|
||||
// 4 "#hhh"
|
||||
// 7 "#hhhhhh"
|
||||
// 10 "#hhhhhhhhh"
|
||||
// 13 "#hhhhhhhhhhhh"
|
||||
// Any other cases will be invalid. Return early in this case.
|
||||
if (!(stringSize == 4 || stringSize == 7 || stringSize == 10 || stringSize == 13))
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto digits = (stringSize - 1) / 3;
|
||||
const auto shift = 16 - 4 * digits;
|
||||
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
const auto val = til::parse_unsigned<unsigned int>(string.substr(i * digits + 1, digits), 16);
|
||||
if (!val)
|
||||
{
|
||||
return std::nullopt;
|
||||
return {};
|
||||
}
|
||||
|
||||
isSharpSignFormat = true;
|
||||
foundXParseColorSpec = true;
|
||||
rgbHexDigitCount = (stringSize - 1) / 3;
|
||||
// > When fewer than 16 bits each are specified, they represent the most significant bits of the value.
|
||||
// > For example, the string "#3a7" is the same as "#3000a0007000".
|
||||
// Source: https://www.x.org/releases/current/doc/man/man3/XQueryColor.3.xhtml
|
||||
// -> Shift the value up.
|
||||
auto v = *val << shift;
|
||||
|
||||
std::advance(curr, 1);
|
||||
// Now that `v` is 16-bit large we can map it to an 8-bit value.
|
||||
v = (v * 255 + 0x7fff) / 0xffff;
|
||||
|
||||
parameters[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
// No valid spec is found. Return early.
|
||||
if (!foundXParseColorSpec)
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Try to parse the actual color value of each component.
|
||||
for (size_t component = 0; component < 3; component++)
|
||||
{
|
||||
auto foundColor = false;
|
||||
auto& parameterValue = til::at(parameterValues, component);
|
||||
// For "sharp sign" format, the rgbHexDigitCount is known.
|
||||
// For "rgb:" format, colorspecs are up to hhhh/hhhh/hhhh, for 1-4 h's
|
||||
const auto iteration = isSharpSignFormat ? rgbHexDigitCount : 4;
|
||||
for (size_t i = 0; i < iteration && curr < string.cend(); i++)
|
||||
{
|
||||
const auto wch = *curr++;
|
||||
|
||||
parameterValue *= 16;
|
||||
unsigned int intVal = 0;
|
||||
const auto ret = HexToUint(wch, intVal);
|
||||
if (!ret)
|
||||
{
|
||||
// Encountered something weird oh no
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
parameterValue += intVal;
|
||||
|
||||
if (isSharpSignFormat)
|
||||
{
|
||||
// If we get this far, any number can be seen as a valid part
|
||||
// of this component.
|
||||
foundColor = true;
|
||||
|
||||
if (i >= rgbHexDigitCount)
|
||||
{
|
||||
// Successfully parsed this component. Start the next one.
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Record the hex digit count of the current component.
|
||||
rgbHexDigitCount = i + 1;
|
||||
|
||||
// If this is the first 2 component...
|
||||
if (component < 2 && curr < string.cend() && *curr == L'/')
|
||||
{
|
||||
// ...and we have successfully parsed this component, we need
|
||||
// to skip the delimiter before starting the next one.
|
||||
curr++;
|
||||
foundColor = true;
|
||||
break;
|
||||
}
|
||||
// Or we have reached the end of the string...
|
||||
else if (curr >= string.cend())
|
||||
{
|
||||
// ...meaning that this is the last component. We're not going to
|
||||
// see any delimiter. We can just break out.
|
||||
foundColor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundColor)
|
||||
{
|
||||
// Indicates there was some error parsing color.
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Calculate the actual color value based on the hex digit count.
|
||||
auto& colorValue = til::at(colorValues, component);
|
||||
const auto scaleMultiplier = isSharpSignFormat ? 0x10 : 0x11;
|
||||
const auto scaleDivisor = scaleMultiplier << 8 >> 4 * (4 - rgbHexDigitCount);
|
||||
colorValue = parameterValue * scaleMultiplier / scaleDivisor;
|
||||
}
|
||||
|
||||
if (curr >= string.cend())
|
||||
{
|
||||
// We're at the end of the string and we have successfully parsed the color.
|
||||
foundValidColorSpec = true;
|
||||
}
|
||||
|
||||
// Only if we find a valid colorspec can we pass it out successfully.
|
||||
if (foundValidColorSpec)
|
||||
{
|
||||
return til::color(LOBYTE(til::at(colorValues, 0)),
|
||||
LOBYTE(til::at(colorValues, 1)),
|
||||
LOBYTE(til::at(colorValues, 2)));
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION();
|
||||
return std::nullopt;
|
||||
return til::color(LOBYTE(til::at(parameters, 0)),
|
||||
LOBYTE(til::at(parameters, 1)),
|
||||
LOBYTE(til::at(parameters, 2)));
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
@ -506,74 +414,6 @@ std::tuple<int, int, int> Utils::ColorToHLS(const til::color color) noexcept
|
||||
return { h, l, s };
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Converts a hex character to its equivalent integer value.
|
||||
// Arguments:
|
||||
// - wch - Character to convert.
|
||||
// - value - receives the int value of the char
|
||||
// Return Value:
|
||||
// - true iff the character is a hex character.
|
||||
bool Utils::HexToUint(const wchar_t wch,
|
||||
unsigned int& value) noexcept
|
||||
{
|
||||
value = 0;
|
||||
auto success = false;
|
||||
if (wch >= L'0' && wch <= L'9')
|
||||
{
|
||||
value = wch - L'0';
|
||||
success = true;
|
||||
}
|
||||
else if (wch >= L'A' && wch <= L'F')
|
||||
{
|
||||
value = (wch - L'A') + 10;
|
||||
success = true;
|
||||
}
|
||||
else if (wch >= L'a' && wch <= L'f')
|
||||
{
|
||||
value = (wch - L'a') + 10;
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Converts a number string to its equivalent unsigned integer value.
|
||||
// Arguments:
|
||||
// - wstr - String to convert.
|
||||
// - value - receives the int value of the string
|
||||
// Return Value:
|
||||
// - true iff the string is a unsigned integer string.
|
||||
bool Utils::StringToUint(const std::wstring_view wstr,
|
||||
unsigned int& value)
|
||||
{
|
||||
if (wstr.size() < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int result = 0;
|
||||
size_t current = 0;
|
||||
while (current < wstr.size())
|
||||
{
|
||||
const auto wch = wstr.at(current);
|
||||
if (_isNumber(wch))
|
||||
{
|
||||
result *= 10;
|
||||
result += wch - L'0';
|
||||
|
||||
++current;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
value = result;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Split a string into different parts using the delimiter provided.
|
||||
// Arguments:
|
||||
@ -581,34 +421,14 @@ bool Utils::StringToUint(const std::wstring_view wstr,
|
||||
// - delimiter - delimiter to use.
|
||||
// Return Value:
|
||||
// - a vector containing the result string parts.
|
||||
std::vector<std::wstring_view> Utils::SplitString(const std::wstring_view wstr,
|
||||
const wchar_t delimiter) noexcept
|
||||
til::small_vector<std::wstring_view, 4> Utils::SplitString(std::wstring_view wstr, const wchar_t delimiter) noexcept
|
||||
try
|
||||
{
|
||||
std::vector<std::wstring_view> result;
|
||||
size_t current = 0;
|
||||
while (current < wstr.size())
|
||||
til::small_vector<std::wstring_view, 4> result;
|
||||
|
||||
for (auto&& part : til::split_iterator{ wstr, delimiter })
|
||||
{
|
||||
const auto nextDelimiter = wstr.find(delimiter, current);
|
||||
if (nextDelimiter == std::wstring::npos)
|
||||
{
|
||||
result.push_back(wstr.substr(current));
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto length = nextDelimiter - current;
|
||||
result.push_back(wstr.substr(current, length));
|
||||
// Skip this part and the delimiter. Start the next one
|
||||
current += length + 1;
|
||||
// The next index is larger than string size, which means the string
|
||||
// is in the format of "part1;part2;" (assuming use ';' as delimiter).
|
||||
// Add the last part which is an empty string.
|
||||
if (current >= wstr.size())
|
||||
{
|
||||
result.push_back(L"");
|
||||
}
|
||||
}
|
||||
result.push_back(part);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -925,6 +745,13 @@ HRESULT Utils::GetOverlappedResultSameThread(const OVERLAPPED* overlapped, DWORD
|
||||
// - a new stable v5 UUID
|
||||
GUID Utils::CreateV5Uuid(const GUID& namespaceGuid, const std::span<const std::byte> name)
|
||||
{
|
||||
static constexpr auto EndianSwap = [](GUID value) {
|
||||
value.Data1 = _byteswap_ulong(value.Data1);
|
||||
value.Data2 = _byteswap_ushort(value.Data2);
|
||||
value.Data3 = _byteswap_ushort(value.Data3);
|
||||
return value;
|
||||
};
|
||||
|
||||
// v5 uuid generation happens over values in network byte order, so let's enforce that
|
||||
auto correctEndianNamespaceGuid{ EndianSwap(namespaceGuid) };
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user