I'm curious if allocating a buffer on the stack is required to have correct alignment for any type, similar to how malloc
works, or if I would be forced to use something like std::aligned_storage
.
Consider the following block of code:
typedef enum _KEY_VALUE_INFORMATION_CLASS {
KeyValueBasicInformation = 0,
// Others
} KEY_VALUE_INFORMATION_CLASS;
typedef struct _KEY_VALUE_BASIC_INFORMATION {
ULONG TitleIndex;
ULONG Type;
ULONG NameLength;
WCHAR Name[1];
} KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION;
std::vector<std::wstring> RegistryKey::EnumerateValueNames() const
{
std::vector<std::wstring> result;
ULONG index = 0;
const ULONG valueNameStructSize = 16384 * sizeof(wchar_t) +
sizeof(KEY_VALUE_BASIC_INFORMATION);
// Stack buffer here
unsigned char buff[valueNameStructSize];
// Casted here
KEY_VALUE_BASIC_INFORMATION const* basicValueInformation =
reinterpret_cast<KEY_VALUE_BASIC_INFORMATION const*>(buff);
for(;;)
{
ULONG resultLength;
NTSTATUS errorCheck = PNtEnumerateValueKeyFunc(
hKey_,
index++,
KeyValueBasicInformation,
buff,
valueNameStructSize,
&resultLength);
if (NT_SUCCESS(errorCheck))
{
result.emplace_back(std::wstring(basicValueInformation->Name,
basicValueInformation->NameLength / sizeof(wchar_t)));
}
else if (errorCheck == STATUS_NO_MORE_ENTRIES)
{
break;
}
else
{
Win32Exception::ThrowFromNtError(errorCheck);
}
}
return result;
}
Note how the value buff
is a character buffer put on the stack, sized to hold a given maximum amount of data. However, I'm concerned that the cast required to interpret the buffer as a string may cause an alignment fault if this code were to be ported to another (say ARM or IA64) platform.
EDIT: If anyone's curious, I redid this in terms of std::aligned_storage
and std::alignment_of
:
std::vector<std::wstring> RegistryKey::EnumerateValueNames() const
{
std::vector<std::wstring> result;
ULONG index = 0;
const ULONG valueNameStructSize = 16384 * sizeof(wchar_t) +
sizeof(KEY_VALUE_BASIC_INFORMATION);
std::aligned_storage<valueNameStructSize,
std::alignment_of<KEY_VALUE_BASIC_INFORMATION>::value>::type buff;
auto basicValueInformation =
reinterpret_cast<KEY_VALUE_BASIC_INFORMATION*>(&buff);
for(;;)
{
ULONG resultLength;
NTSTATUS errorCheck = PNtEnumerateValueKeyFunc(
hKey_,
index++,
KeyValueBasicInformation,
basicValueInformation,
valueNameStructSize,
&resultLength);
if (NT_SUCCESS(errorCheck))
{
result.emplace_back(std::wstring(basicValueInformation->Name,
basicValueInformation->NameLength / sizeof(wchar_t)));
}
else if (errorCheck == STATUS_NO_MORE_ENTRIES)
{
break;
}
else
{
Win32Exception::ThrowFromNtError(errorCheck);
}
}
return std::move(result);
}
The standard makes no requirements on the alignment of automatic variables (or variables with static storage for that matter), other than the compiler must make sure that accessing them works.
Object types have alignment requirements (3.9.1, 3.9.2). The alignment of a complete object type is an implementation-defined integer value representing a number of bytes; an object is allocated at an address that meets the alignment requirements of its object type
Note: "object type" here means a type that's not a function, reference or void type, (i.e., it applies to unsigned char
).
One way to get an aligned buffer might be to declare buff
like so:
KEY_VALUE_BASIC_INFORMATION buff[valueNameStructSize/sizeof(KEY_VALUE_BASIC_INFORMATION) + 1];
And you'll be able to get rid of the reinterpret_cast<>
to boot.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With