Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wil::to_array_view provides safer access to IBuffer / IMemoryBuffer #359

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions include/wil/cppwinrt_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,48 @@ namespace wil::details
#endif // __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS
/// @endcond

#if defined(WINRT_Windows_Foundation_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_HELPERS)
#define __WIL_CPPWINRT_WINDOWS_FOUNDATION_HELPERS
namespace Windows::Foundation
dunhor marked this conversation as resolved.
Show resolved Hide resolved
{
/// @cond
struct IMemoryBufferByteAccess;
/// @endcond
}

namespace wil
{
//! Returns a view into the underlying bytes of a memory buffer
//! provided in the form of an IMemoryBufferReference.
//! The caller is responsible for ensuring that the memory buffer's
//! lifetime encompasses the lifetime of the returned view.
//! By default, returns an array_view<uint8_t>, but you can provide an alternate
//! type such as to_array_view<double>.
//! You must include memorybuffer.h in order to use this overload of to_array_view.
oldnewthing marked this conversation as resolved.
Show resolved Hide resolved
template<typename T = uint8_t, int V = 0>
oldnewthing marked this conversation as resolved.
Show resolved Hide resolved
winrt::array_view<T> to_array_view(winrt::Windows::Foundation::IMemoryBufferReference const& reference)
{
uint8_t* data;
uint32_t capacity;
winrt::check_hresult(reference.as<std::enable_if_t<!V, ::Windows::Foundation::IMemoryBufferByteAccess>>()->GetBuffer(&data, &capacity));
oldnewthing marked this conversation as resolved.
Show resolved Hide resolved
return { reinterpret_cast<T*>(data), static_cast<uint32_t>(capacity / sizeof(T)) };
}

//! Returns a view into the underlying bytes of a memory buffer
//! provided in the form of an IMemoryBuffer.
//! The caller is responsible for ensuring that the memory buffer's
//! lifetime encompasses the lifetime of the returned view.
//! By default, returns an array_view<uint8_t>, but you can provide an alternate
//! type such as to_array_view<double>.
//! You must include memorybuffer.h in order to use this overload of to_array_view.
template<typename T = uint8_t, int V = 0>
winrt::array_view<T> to_array_view(winrt::Windows::Foundation::IMemoryBuffer const& buffer)
{
return to_array_view<T, V>(buffer.CreateReference());
}
}
#endif

#if defined(WINRT_Windows_Foundation_Collections_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS)
#define __WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS
namespace wil
Expand Down Expand Up @@ -314,6 +356,34 @@ namespace wil
}
#endif

#if defined(WINRT_Windows_Storage_Streams_H) && !defined(__WIL_CPPWINRT_WINDOWS_STORAGE_STREAMS_HELPERS)
#define __WIL_CPPWINRT_WINDOWS_STORAGE_STREAMS_HELPERS
namespace wil
{
//! Returns a view into the underlying bytes of an IBuffer up to its Length.
//! The caller is responsible for ensuring that the IBuffer's
//! lifetime encompasses the lifetime of the returned view.
//! By default, returns an array_view<uint8_t>, but you can provide an alternate
//! type such as to_array_view<double>.
template<typename T = uint8_t>
winrt::array_view<T> to_array_view(winrt::Windows::Storage::Streams::IBuffer const& buffer)
{
return { reinterpret_cast<T*>(buffer.data()), static_cast<uint32_t>(buffer.Length() / sizeof(T)) };
}

//! Returns a view into the underlying bytes of an IBuffer up to its Capacity.
//! The caller is responsible for ensuring that the IBuffer's
//! lifetime encompasses the lifetime of the returned view.
//! By default, returns an array_view<uint8_t>, but you can provide an alternate
//! type such as to_array_view<double>.
template<typename T = uint8_t>
winrt::array_view<T> to_array_view_for_capacity(winrt::Windows::Storage::Streams::IBuffer const& buffer)
{
return { reinterpret_cast<T*>(buffer.data()), static_cast<uint32_t>(buffer.Capacity() / sizeof(T)) };
}
}
#endif

#if defined(WINRT_Windows_UI_H) && defined(_WINDOWS_UI_INTEROP_H_) && !defined(__WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS)
#define __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS
#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX)
Expand Down
38 changes: 38 additions & 0 deletions tests/CppWinRTTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#include <winrt/Windows.ApplicationModel.Activation.h>
#include <wil/cppwinrt_helpers.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.Storage.Streams.h>
#include <wil/cppwinrt_helpers.h> // Verify can include a second time to unlock more features
#include <memorybuffer.h>

using namespace winrt::Windows::ApplicationModel::Activation;

Expand Down Expand Up @@ -182,6 +184,42 @@ TEST_CASE("CppWinRTTests::VectorToVector", "[cppwinrt]")
winrt::uninit_apartment();
}

TEST_CASE("CppWinRTTests::BufferToArrayView", "[cppwinrt]")
{
// Create a buffer of capacity 8 and length 4.
auto buffer = winrt::Windows::Storage::Streams::Buffer(8);
buffer.Length(4);
// Get a Capacity-based int view and set the ints to 256 and 512.
{
auto view = wil::to_array_view_for_capacity<int32_t>(buffer);
REQUIRE(view.size() == 2);
view[0] = 256;
oldnewthing marked this conversation as resolved.
Show resolved Hide resolved
view[1] = 512;
}
// Get a Length-based byte view and confirm that the four bytes are { 0, 1, 0, 0 }.
// (Assumes little-endian system.)
{
auto view = wil::to_array_view(buffer);
REQUIRE(view.size() == 4);
REQUIRE(view == winrt::array_view(std::array<uint8_t, 4>{ 0, 1, 0, 0 }));
}
// Create an IMemoryBuffer around the Buffer.
auto mbuffer = winrt::Windows::Storage::Streams::Buffer::CreateMemoryBufferOverIBuffer(buffer);
// Verify that the buffer is the 2 ints 256 and 512.
{
auto view = wil::to_array_view<int>(mbuffer);
REQUIRE(view.size() == 2);
REQUIRE(view == winrt::array_view(std::array{ 256, 512 }));
}
// Verify that the buffer reference is the 8 bytes { 0, 1, 0, 0, 0, 2, 0, 0 }.
// (Assumes little-endian system.)
{
auto view = wil::to_array_view(mbuffer.CreateReference());
REQUIRE(view.size() == 8);
REQUIRE(view == winrt::array_view(std::array<uint8_t, 8>{ 0, 1, 0, 0, 0, 2, 0, 0 }));
}
}

TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]")
{
auto test = [](HRESULT hr)
Expand Down
Loading