Skip to content

Commit

Permalink
Add held-buffer converter helpers for hot paths.
Browse files Browse the repository at this point in the history
  • Loading branch information
miniksa committed Jan 8, 2021
1 parent ead8fab commit 00f80e4
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 12 deletions.
70 changes: 58 additions & 12 deletions src/types/convert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,38 @@ static const WORD leftShiftScanCode = 0x2A;
// - The UTF-16 wide string.
// - NOTE: Throws suitable HRESULT errors from memory allocation, safe math, or MultiByteToWideChar failures.
[[nodiscard]] std::wstring ConvertToW(const UINT codePage, const std::string_view source)
{
// Make a buffer on behalf of the caller.
std::wstring out;

// Call the other form of the function.
ConvertToW(codePage, source, out);

// Return as a wstring
return out;
}

// Routine Description:
// - Takes a multibyte string, allocates the appropriate amount of memory for the conversion, performs the conversion,
// and returns the Unicode UTF-16 result in the smart pointer (and the length).
// - NOTE: This form exists so a frequent caller with a hot path can cache their string
// buffer between calls instead of letting it get new/deleted in a tight loop.
// Arguments:
// - codepage - Windows Code Page representing the multibyte source text
// - source - View of multibyte characters of source text
// - outBuffer - The buffer to fill with converted wide string data.
// Return Value:
// - The UTF-16 wide string.
// - NOTE: Throws suitable HRESULT errors from memory allocation, safe math, or MultiByteToWideChar failures.
[[nodiscard]] void ConvertToW(const UINT codePage,
const std::string_view source,
std::wstring& outBuffer)
{
// If there's nothing to convert, bail early.
if (source.empty())
{
return {};
outBuffer.clear();
return;
}

int iSource; // convert to int because Mb2Wc requires it.
Expand All @@ -52,11 +79,28 @@ static const WORD leftShiftScanCode = 0x2A;
THROW_IF_FAILED(IntToSizeT(iTarget, &cchNeeded));

// Allocate ourselves some space
std::wstring out;
out.resize(cchNeeded);
outBuffer.resize(cchNeeded);

// Attempt conversion for real.
THROW_LAST_ERROR_IF_AND_IGNORE_BAD_GLE(0 == MultiByteToWideChar(codePage, 0, source.data(), iSource, out.data(), iTarget));
THROW_LAST_ERROR_IF_AND_IGNORE_BAD_GLE(0 == MultiByteToWideChar(codePage, 0, source.data(), iSource, outBuffer.data(), iTarget));
}

// Routine Description:
// - Takes a wide string, allocates the appropriate amount of memory for the conversion, performs the conversion,
// and returns the Multibyte result
// Arguments:
// - codepage - Windows Code Page representing the multibyte destination text
// - source - Unicode (UTF-16) characters of source text
// Return Value:
// - The multibyte string encoded in the given codepage
// - NOTE: Throws suitable HRESULT errors from memory allocation, safe math, or MultiByteToWideChar failures.
[[nodiscard]] std::string ConvertToA(const UINT codepage, const std::wstring_view source)
{
// Make a buffer on behalf of the caller.
std::string out;

// Call the other form of the function.
ConvertToA(codepage, source, out);

// Return as a string
return out;
Expand All @@ -65,18 +109,24 @@ static const WORD leftShiftScanCode = 0x2A;
// Routine Description:
// - Takes a wide string, allocates the appropriate amount of memory for the conversion, performs the conversion,
// and returns the Multibyte result
// - NOTE: This form exists so a frequent caller with a hot path can cache their string
// buffer between calls instead of letting it get new/deleted in a tight loop.
// Arguments:
// - codepage - Windows Code Page representing the multibyte destination text
// - source - Unicode (UTF-16) characters of source text
// - outBuffer - The buffer to fill with converted string data.
// Return Value:
// - The multibyte string encoded in the given codepage
// - NOTE: Throws suitable HRESULT errors from memory allocation, safe math, or MultiByteToWideChar failures.
[[nodiscard]] std::string ConvertToA(const UINT codepage, const std::wstring_view source)
[[nodiscard]] void ConvertToA(const UINT codepage,
const std::wstring_view source,
std::string& outBuffer)
{
// If there's nothing to convert, bail early.
if (source.empty())
{
return {};
outBuffer.clear();
return;
}

int iSource; // convert to int because Wc2Mb requires it.
Expand All @@ -93,17 +143,13 @@ static const WORD leftShiftScanCode = 0x2A;
THROW_IF_FAILED(IntToSizeT(iTarget, &cchNeeded));

// Allocate ourselves some space
std::string out;
out.resize(cchNeeded);
outBuffer.resize(cchNeeded);

// Attempt conversion for real.
// clang-format off
#pragma prefast(suppress: __WARNING_W2A_BEST_FIT, "WC_NO_BEST_FIT_CHARS doesn't work in many codepages. Retain old behavior.")
// clang-format on
THROW_LAST_ERROR_IF(0 == WideCharToMultiByte(codepage, 0, source.data(), iSource, out.data(), iTarget, nullptr, nullptr));

// Return as a string
return out;
THROW_LAST_ERROR_IF(0 == WideCharToMultiByte(codepage, 0, source.data(), iSource, outBuffer.data(), iTarget, nullptr, nullptr));
}

// Routine Description:
Expand Down
11 changes: 11 additions & 0 deletions src/types/inc/convert.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ enum class CodepointWidth : BYTE
[[nodiscard]] std::string ConvertToA(const UINT codepage,
const std::wstring_view source);

// These out-param forms of the functions exist so a frequent caller on a hot
// codepath can hold their string buffer between calls instead of
// letting us allocate one for it to be thrown away shortly after.
[[nodiscard]] void ConvertToW(const UINT codepage,
const std::string_view source,
std::wstring& outBuffer);

[[nodiscard]] void ConvertToA(const UINT codepage,
const std::wstring_view source,
std::string& outBuffer);

[[nodiscard]] size_t GetALengthFromW(const UINT codepage,
const std::wstring_view source);

Expand Down

0 comments on commit 00f80e4

Please sign in to comment.