terminal/src/types/utils.cpp
Dustin L. Howett 09471c3753
Replace gsl::at with a new til::at(span) for pre-checked bounds (#6925)
The recent changes to use gsl::span everywhere added a few bounds checks
along codepaths where we were already checking bounds. Some of them may
be non-obvious to the optimizer, so we can now use til::at to help them
along.

To accomplish this, I've added a new overload of til::at that takes a
span and directly accesses its backing buffer.
2020-07-15 10:29:36 -07:00

487 lines
18 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "inc/utils.hpp"
using namespace Microsoft::Console;
static constexpr std::array<til::color, 16> campbellColorTable{
til::color{ 0x0C, 0x0C, 0x0C },
til::color{ 0xC5, 0x0F, 0x1F },
til::color{ 0x13, 0xA1, 0x0E },
til::color{ 0xC1, 0x9C, 0x00 },
til::color{ 0x00, 0x37, 0xDA },
til::color{ 0x88, 0x17, 0x98 },
til::color{ 0x3A, 0x96, 0xDD },
til::color{ 0xCC, 0xCC, 0xCC },
til::color{ 0x76, 0x76, 0x76 },
til::color{ 0xE7, 0x48, 0x56 },
til::color{ 0x16, 0xC6, 0x0C },
til::color{ 0xF9, 0xF1, 0xA5 },
til::color{ 0x3B, 0x78, 0xFF },
til::color{ 0xB4, 0x00, 0x9E },
til::color{ 0x61, 0xD6, 0xD6 },
til::color{ 0xF2, 0xF2, 0xF2 },
};
static constexpr std::array<til::color, 256> standardXterm256ColorTable{
til::color{ 0x00, 0x00, 0x00 },
til::color{ 0x80, 0x00, 0x00 },
til::color{ 0x00, 0x80, 0x00 },
til::color{ 0x80, 0x80, 0x00 },
til::color{ 0x00, 0x00, 0x80 },
til::color{ 0x80, 0x00, 0x80 },
til::color{ 0x00, 0x80, 0x80 },
til::color{ 0xC0, 0xC0, 0xC0 },
til::color{ 0x80, 0x80, 0x80 },
til::color{ 0xFF, 0x00, 0x00 },
til::color{ 0x00, 0xFF, 0x00 },
til::color{ 0xFF, 0xFF, 0x00 },
til::color{ 0x00, 0x00, 0xFF },
til::color{ 0xFF, 0x00, 0xFF },
til::color{ 0x00, 0xFF, 0xFF },
til::color{ 0xFF, 0xFF, 0xFF },
til::color{ 0x00, 0x00, 0x00 },
til::color{ 0x00, 0x00, 0x5F },
til::color{ 0x00, 0x00, 0x87 },
til::color{ 0x00, 0x00, 0xAF },
til::color{ 0x00, 0x00, 0xD7 },
til::color{ 0x00, 0x00, 0xFF },
til::color{ 0x00, 0x5F, 0x00 },
til::color{ 0x00, 0x5F, 0x5F },
til::color{ 0x00, 0x5F, 0x87 },
til::color{ 0x00, 0x5F, 0xAF },
til::color{ 0x00, 0x5F, 0xD7 },
til::color{ 0x00, 0x5F, 0xFF },
til::color{ 0x00, 0x87, 0x00 },
til::color{ 0x00, 0x87, 0x5F },
til::color{ 0x00, 0x87, 0x87 },
til::color{ 0x00, 0x87, 0xAF },
til::color{ 0x00, 0x87, 0xD7 },
til::color{ 0x00, 0x87, 0xFF },
til::color{ 0x00, 0xAF, 0x00 },
til::color{ 0x00, 0xAF, 0x5F },
til::color{ 0x00, 0xAF, 0x87 },
til::color{ 0x00, 0xAF, 0xAF },
til::color{ 0x00, 0xAF, 0xD7 },
til::color{ 0x00, 0xAF, 0xFF },
til::color{ 0x00, 0xD7, 0x00 },
til::color{ 0x00, 0xD7, 0x5F },
til::color{ 0x00, 0xD7, 0x87 },
til::color{ 0x00, 0xD7, 0xAF },
til::color{ 0x00, 0xD7, 0xD7 },
til::color{ 0x00, 0xD7, 0xFF },
til::color{ 0x00, 0xFF, 0x00 },
til::color{ 0x00, 0xFF, 0x5F },
til::color{ 0x00, 0xFF, 0x87 },
til::color{ 0x00, 0xFF, 0xAF },
til::color{ 0x00, 0xFF, 0xD7 },
til::color{ 0x00, 0xFF, 0xFF },
til::color{ 0x5F, 0x00, 0x00 },
til::color{ 0x5F, 0x00, 0x5F },
til::color{ 0x5F, 0x00, 0x87 },
til::color{ 0x5F, 0x00, 0xAF },
til::color{ 0x5F, 0x00, 0xD7 },
til::color{ 0x5F, 0x00, 0xFF },
til::color{ 0x5F, 0x5F, 0x00 },
til::color{ 0x5F, 0x5F, 0x5F },
til::color{ 0x5F, 0x5F, 0x87 },
til::color{ 0x5F, 0x5F, 0xAF },
til::color{ 0x5F, 0x5F, 0xD7 },
til::color{ 0x5F, 0x5F, 0xFF },
til::color{ 0x5F, 0x87, 0x00 },
til::color{ 0x5F, 0x87, 0x5F },
til::color{ 0x5F, 0x87, 0x87 },
til::color{ 0x5F, 0x87, 0xAF },
til::color{ 0x5F, 0x87, 0xD7 },
til::color{ 0x5F, 0x87, 0xFF },
til::color{ 0x5F, 0xAF, 0x00 },
til::color{ 0x5F, 0xAF, 0x5F },
til::color{ 0x5F, 0xAF, 0x87 },
til::color{ 0x5F, 0xAF, 0xAF },
til::color{ 0x5F, 0xAF, 0xD7 },
til::color{ 0x5F, 0xAF, 0xFF },
til::color{ 0x5F, 0xD7, 0x00 },
til::color{ 0x5F, 0xD7, 0x5F },
til::color{ 0x5F, 0xD7, 0x87 },
til::color{ 0x5F, 0xD7, 0xAF },
til::color{ 0x5F, 0xD7, 0xD7 },
til::color{ 0x5F, 0xD7, 0xFF },
til::color{ 0x5F, 0xFF, 0x00 },
til::color{ 0x5F, 0xFF, 0x5F },
til::color{ 0x5F, 0xFF, 0x87 },
til::color{ 0x5F, 0xFF, 0xAF },
til::color{ 0x5F, 0xFF, 0xD7 },
til::color{ 0x5F, 0xFF, 0xFF },
til::color{ 0x87, 0x00, 0x00 },
til::color{ 0x87, 0x00, 0x5F },
til::color{ 0x87, 0x00, 0x87 },
til::color{ 0x87, 0x00, 0xAF },
til::color{ 0x87, 0x00, 0xD7 },
til::color{ 0x87, 0x00, 0xFF },
til::color{ 0x87, 0x5F, 0x00 },
til::color{ 0x87, 0x5F, 0x5F },
til::color{ 0x87, 0x5F, 0x87 },
til::color{ 0x87, 0x5F, 0xAF },
til::color{ 0x87, 0x5F, 0xD7 },
til::color{ 0x87, 0x5F, 0xFF },
til::color{ 0x87, 0x87, 0x00 },
til::color{ 0x87, 0x87, 0x5F },
til::color{ 0x87, 0x87, 0x87 },
til::color{ 0x87, 0x87, 0xAF },
til::color{ 0x87, 0x87, 0xD7 },
til::color{ 0x87, 0x87, 0xFF },
til::color{ 0x87, 0xAF, 0x00 },
til::color{ 0x87, 0xAF, 0x5F },
til::color{ 0x87, 0xAF, 0x87 },
til::color{ 0x87, 0xAF, 0xAF },
til::color{ 0x87, 0xAF, 0xD7 },
til::color{ 0x87, 0xAF, 0xFF },
til::color{ 0x87, 0xD7, 0x00 },
til::color{ 0x87, 0xD7, 0x5F },
til::color{ 0x87, 0xD7, 0x87 },
til::color{ 0x87, 0xD7, 0xAF },
til::color{ 0x87, 0xD7, 0xD7 },
til::color{ 0x87, 0xD7, 0xFF },
til::color{ 0x87, 0xFF, 0x00 },
til::color{ 0x87, 0xFF, 0x5F },
til::color{ 0x87, 0xFF, 0x87 },
til::color{ 0x87, 0xFF, 0xAF },
til::color{ 0x87, 0xFF, 0xD7 },
til::color{ 0x87, 0xFF, 0xFF },
til::color{ 0xAF, 0x00, 0x00 },
til::color{ 0xAF, 0x00, 0x5F },
til::color{ 0xAF, 0x00, 0x87 },
til::color{ 0xAF, 0x00, 0xAF },
til::color{ 0xAF, 0x00, 0xD7 },
til::color{ 0xAF, 0x00, 0xFF },
til::color{ 0xAF, 0x5F, 0x00 },
til::color{ 0xAF, 0x5F, 0x5F },
til::color{ 0xAF, 0x5F, 0x87 },
til::color{ 0xAF, 0x5F, 0xAF },
til::color{ 0xAF, 0x5F, 0xD7 },
til::color{ 0xAF, 0x5F, 0xFF },
til::color{ 0xAF, 0x87, 0x00 },
til::color{ 0xAF, 0x87, 0x5F },
til::color{ 0xAF, 0x87, 0x87 },
til::color{ 0xAF, 0x87, 0xAF },
til::color{ 0xAF, 0x87, 0xD7 },
til::color{ 0xAF, 0x87, 0xFF },
til::color{ 0xAF, 0xAF, 0x00 },
til::color{ 0xAF, 0xAF, 0x5F },
til::color{ 0xAF, 0xAF, 0x87 },
til::color{ 0xAF, 0xAF, 0xAF },
til::color{ 0xAF, 0xAF, 0xD7 },
til::color{ 0xAF, 0xAF, 0xFF },
til::color{ 0xAF, 0xD7, 0x00 },
til::color{ 0xAF, 0xD7, 0x5F },
til::color{ 0xAF, 0xD7, 0x87 },
til::color{ 0xAF, 0xD7, 0xAF },
til::color{ 0xAF, 0xD7, 0xD7 },
til::color{ 0xAF, 0xD7, 0xFF },
til::color{ 0xAF, 0xFF, 0x00 },
til::color{ 0xAF, 0xFF, 0x5F },
til::color{ 0xAF, 0xFF, 0x87 },
til::color{ 0xAF, 0xFF, 0xAF },
til::color{ 0xAF, 0xFF, 0xD7 },
til::color{ 0xAF, 0xFF, 0xFF },
til::color{ 0xD7, 0x00, 0x00 },
til::color{ 0xD7, 0x00, 0x5F },
til::color{ 0xD7, 0x00, 0x87 },
til::color{ 0xD7, 0x00, 0xAF },
til::color{ 0xD7, 0x00, 0xD7 },
til::color{ 0xD7, 0x00, 0xFF },
til::color{ 0xD7, 0x5F, 0x00 },
til::color{ 0xD7, 0x5F, 0x5F },
til::color{ 0xD7, 0x5F, 0x87 },
til::color{ 0xD7, 0x5F, 0xAF },
til::color{ 0xD7, 0x5F, 0xD7 },
til::color{ 0xD7, 0x5F, 0xFF },
til::color{ 0xD7, 0x87, 0x00 },
til::color{ 0xD7, 0x87, 0x5F },
til::color{ 0xD7, 0x87, 0x87 },
til::color{ 0xD7, 0x87, 0xAF },
til::color{ 0xD7, 0x87, 0xD7 },
til::color{ 0xD7, 0x87, 0xFF },
til::color{ 0xD7, 0xAF, 0x00 },
til::color{ 0xD7, 0xAF, 0x5F },
til::color{ 0xD7, 0xAF, 0x87 },
til::color{ 0xD7, 0xAF, 0xAF },
til::color{ 0xD7, 0xAF, 0xD7 },
til::color{ 0xD7, 0xAF, 0xFF },
til::color{ 0xD7, 0xD7, 0x00 },
til::color{ 0xD7, 0xD7, 0x5F },
til::color{ 0xD7, 0xD7, 0x87 },
til::color{ 0xD7, 0xD7, 0xAF },
til::color{ 0xD7, 0xD7, 0xD7 },
til::color{ 0xD7, 0xD7, 0xFF },
til::color{ 0xD7, 0xFF, 0x00 },
til::color{ 0xD7, 0xFF, 0x5F },
til::color{ 0xD7, 0xFF, 0x87 },
til::color{ 0xD7, 0xFF, 0xAF },
til::color{ 0xD7, 0xFF, 0xD7 },
til::color{ 0xD7, 0xFF, 0xFF },
til::color{ 0xFF, 0x00, 0x00 },
til::color{ 0xFF, 0x00, 0x5F },
til::color{ 0xFF, 0x00, 0x87 },
til::color{ 0xFF, 0x00, 0xAF },
til::color{ 0xFF, 0x00, 0xD7 },
til::color{ 0xFF, 0x00, 0xFF },
til::color{ 0xFF, 0x5F, 0x00 },
til::color{ 0xFF, 0x5F, 0x5F },
til::color{ 0xFF, 0x5F, 0x87 },
til::color{ 0xFF, 0x5F, 0xAF },
til::color{ 0xFF, 0x5F, 0xD7 },
til::color{ 0xFF, 0x5F, 0xFF },
til::color{ 0xFF, 0x87, 0x00 },
til::color{ 0xFF, 0x87, 0x5F },
til::color{ 0xFF, 0x87, 0x87 },
til::color{ 0xFF, 0x87, 0xAF },
til::color{ 0xFF, 0x87, 0xD7 },
til::color{ 0xFF, 0x87, 0xFF },
til::color{ 0xFF, 0xAF, 0x00 },
til::color{ 0xFF, 0xAF, 0x5F },
til::color{ 0xFF, 0xAF, 0x87 },
til::color{ 0xFF, 0xAF, 0xAF },
til::color{ 0xFF, 0xAF, 0xD7 },
til::color{ 0xFF, 0xAF, 0xFF },
til::color{ 0xFF, 0xD7, 0x00 },
til::color{ 0xFF, 0xD7, 0x5F },
til::color{ 0xFF, 0xD7, 0x87 },
til::color{ 0xFF, 0xD7, 0xAF },
til::color{ 0xFF, 0xD7, 0xD7 },
til::color{ 0xFF, 0xD7, 0xFF },
til::color{ 0xFF, 0xFF, 0x00 },
til::color{ 0xFF, 0xFF, 0x5F },
til::color{ 0xFF, 0xFF, 0x87 },
til::color{ 0xFF, 0xFF, 0xAF },
til::color{ 0xFF, 0xFF, 0xD7 },
til::color{ 0xFF, 0xFF, 0xFF },
til::color{ 0x08, 0x08, 0x08 },
til::color{ 0x12, 0x12, 0x12 },
til::color{ 0x1C, 0x1C, 0x1C },
til::color{ 0x26, 0x26, 0x26 },
til::color{ 0x30, 0x30, 0x30 },
til::color{ 0x3A, 0x3A, 0x3A },
til::color{ 0x44, 0x44, 0x44 },
til::color{ 0x4E, 0x4E, 0x4E },
til::color{ 0x58, 0x58, 0x58 },
til::color{ 0x62, 0x62, 0x62 },
til::color{ 0x6C, 0x6C, 0x6C },
til::color{ 0x76, 0x76, 0x76 },
til::color{ 0x80, 0x80, 0x80 },
til::color{ 0x8A, 0x8A, 0x8A },
til::color{ 0x94, 0x94, 0x94 },
til::color{ 0x9E, 0x9E, 0x9E },
til::color{ 0xA8, 0xA8, 0xA8 },
til::color{ 0xB2, 0xB2, 0xB2 },
til::color{ 0xBC, 0xBC, 0xBC },
til::color{ 0xC6, 0xC6, 0xC6 },
til::color{ 0xD0, 0xD0, 0xD0 },
til::color{ 0xDA, 0xDA, 0xDA },
til::color{ 0xE4, 0xE4, 0xE4 },
til::color{ 0xEE, 0xEE, 0xEE },
};
// Function Description:
// - Creates a String representation of a guid, in the format
// "{12345678-ABCD-EF12-3456-7890ABCDEF12}"
// Arguments:
// - guid: the GUID to create the string for
// Return Value:
// - a string representation of the GUID. On failure, throws E_INVALIDARG.
std::wstring Utils::GuidToString(const GUID guid)
{
return wil::str_printf<std::wstring>(L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
}
// Method Description:
// - Parses a GUID from a string representation of the GUID. Throws an exception
// if it fails to parse the GUID. See documentation of IIDFromString for
// details.
// Arguments:
// - wstr: a string representation of the GUID to parse
// Return Value:
// - A GUID if the string could successfully be parsed. On failure, throws the
// failing HRESULT.
GUID Utils::GuidFromString(const std::wstring wstr)
{
GUID result{};
THROW_IF_FAILED(IIDFromString(wstr.c_str(), &result));
return result;
}
// Method Description:
// - Creates a GUID, but not via an out parameter.
// Return Value:
// - A GUID if there's enough randomness; otherwise, an exception.
GUID Utils::CreateGuid()
{
GUID result{};
THROW_IF_FAILED(::CoCreateGuid(&result));
return result;
}
// Function Description:
// - Creates a String representation of a color, in the format "#RRGGBB"
// Arguments:
// - color: the COLORREF to create the string for
// Return Value:
// - a string representation of the color
std::string Utils::ColorToHexString(const til::color color)
{
std::stringstream ss;
ss << "#" << std::uppercase << std::setfill('0') << std::hex;
// Force the compiler to promote from byte to int. Without it, the
// stringstream will try to write the components as chars
ss << std::setw(2) << static_cast<int>(color.r);
ss << std::setw(2) << static_cast<int>(color.g);
ss << std::setw(2) << static_cast<int>(color.b);
return ss.str();
}
// Function Description:
// - Parses a color from a string. The string should be in the format "#RRGGBB" or "#RGB"
// Arguments:
// - str: a string representation of the COLORREF to parse
// Return Value:
// - A COLORREF if the string could successfully be parsed. If the string is not
// the correct format, throws E_INVALIDARG
til::color Utils::ColorFromHexString(const std::string_view str)
{
THROW_HR_IF(E_INVALIDARG, str.size() != 7 && str.size() != 4);
THROW_HR_IF(E_INVALIDARG, str.at(0) != '#');
std::string rStr;
std::string gStr;
std::string bStr;
if (str.size() == 4)
{
rStr = std::string(2, str.at(1));
gStr = std::string(2, str.at(2));
bStr = std::string(2, str.at(3));
}
else
{
rStr = std::string(&str.at(1), 2);
gStr = std::string(&str.at(3), 2);
bStr = std::string(&str.at(5), 2);
}
const BYTE r = gsl::narrow_cast<BYTE>(std::stoul(rStr, nullptr, 16));
const BYTE g = gsl::narrow_cast<BYTE>(std::stoul(gStr, nullptr, 16));
const BYTE b = gsl::narrow_cast<BYTE>(std::stoul(bStr, nullptr, 16));
return til::color{ r, g, b };
}
// Routine Description:
// - Shorthand check if a handle value is null or invalid.
// Arguments:
// - Handle
// Return Value:
// - True if non zero and not set to invalid magic value. False otherwise.
bool Utils::IsValidHandle(const HANDLE handle) noexcept
{
return handle != nullptr && handle != INVALID_HANDLE_VALUE;
}
// Function Description:
// - Fill the first 16 entries of a given color table with the Campbell color
// scheme, in the ANSI/VT RGB order.
// Arguments:
// - table: a color table with at least 16 entries
// Return Value:
// - <none>, throws if the table has less that 16 entries
void Utils::InitializeCampbellColorTable(const gsl::span<COLORREF> table)
{
THROW_HR_IF(E_INVALIDARG, table.size() < 16);
std::copy(campbellColorTable.begin(), campbellColorTable.end(), table.begin());
}
// Function Description:
// - Fill the first 16 entries of a given color table with the Campbell color
// scheme, in the Windows BGR order.
// Arguments:
// - table: a color table with at least 16 entries
// Return Value:
// - <none>, throws if the table has less that 16 entries
void Utils::InitializeCampbellColorTableForConhost(const gsl::span<COLORREF> table)
{
THROW_HR_IF(E_INVALIDARG, table.size() < 16);
InitializeCampbellColorTable(table);
SwapANSIColorOrderForConhost(table);
}
// Function Description:
// - modifies in-place the given color table from ANSI (RGB) order to Console order (BRG).
// Arguments:
// - table: a color table with at least 16 entries
// Return Value:
// - <none>, throws if the table has less that 16 entries
void Utils::SwapANSIColorOrderForConhost(const gsl::span<COLORREF> table)
{
THROW_HR_IF(E_INVALIDARG, table.size() < 16);
std::swap(til::at(table, 1), til::at(table, 4));
std::swap(til::at(table, 3), til::at(table, 6));
std::swap(til::at(table, 9), til::at(table, 12));
std::swap(til::at(table, 11), til::at(table, 14));
}
// Function Description:
// - Fill the first 255 entries of a given color table with the default values
// of a full 256-color table
// Arguments:
// - table: a color table with at least 256 entries
// Return Value:
// - <none>, throws if the table has less that 256 entries
void Utils::Initialize256ColorTable(const gsl::span<COLORREF> table)
{
THROW_HR_IF(E_INVALIDARG, table.size() < 256);
std::copy(standardXterm256ColorTable.begin(), standardXterm256ColorTable.end(), table.begin());
}
// Function Description:
// - Generate a Version 5 UUID (specified in RFC4122 4.3)
// v5 UUIDs are stable given the same namespace and "name".
// Arguments:
// - namespaceGuid: The GUID of the v5 UUID namespace, which provides both
// a seed and a tacit agreement that all UUIDs generated
// with it will follow the same data format.
// - name: Bytes comprising the name (in a namespace-specific format)
// Return Value:
// - a new stable v5 UUID
GUID Utils::CreateV5Uuid(const GUID& namespaceGuid, const gsl::span<const gsl::byte> name)
{
// v5 uuid generation happens over values in network byte order, so let's enforce that
auto correctEndianNamespaceGuid{ EndianSwap(namespaceGuid) };
wil::unique_bcrypt_hash hash;
THROW_IF_NTSTATUS_FAILED(BCryptCreateHash(BCRYPT_SHA1_ALG_HANDLE, &hash, nullptr, 0, nullptr, 0, 0));
// According to N4713 8.2.1.11 [basic.lval], accessing the bytes underlying an object
// through unsigned char or char pointer *is defined*.
THROW_IF_NTSTATUS_FAILED(BCryptHashData(hash.get(), reinterpret_cast<PUCHAR>(&correctEndianNamespaceGuid), sizeof(GUID), 0));
// BCryptHashData is ill-specified in that it leaves off "const" qualification for pbInput
THROW_IF_NTSTATUS_FAILED(BCryptHashData(hash.get(), reinterpret_cast<PUCHAR>(const_cast<gsl::byte*>(name.data())), gsl::narrow<ULONG>(name.size()), 0));
std::array<uint8_t, 20> buffer;
THROW_IF_NTSTATUS_FAILED(BCryptFinishHash(hash.get(), buffer.data(), gsl::narrow<ULONG>(buffer.size()), 0));
buffer.at(6) = (buffer.at(6) & 0x0F) | 0x50; // set the uuid version to 5
buffer.at(8) = (buffer.at(8) & 0x3F) | 0x80; // set the variant to 2 (RFC4122)
// We're using memcpy here pursuant to N4713 6.7.2/3 [basic.types],
// "...the underlying bytes making up the object can be copied into an array
// of char or unsigned char...array is copied back into the object..."
// std::copy may compile down to ::memcpy for these types, but using it might
// contravene the standard and nobody's got time for that.
GUID newGuid{ 0 };
::memcpy_s(&newGuid, sizeof(GUID), buffer.data(), sizeof(GUID));
return EndianSwap(newGuid);
}