Skip to content

Commit

Permalink
ibuffer byte access (#138)
Browse files Browse the repository at this point in the history
* initial implementation

* move to c definitions and more swift stuff

* building

* build fixes

* fix assigning proper variable

* adding tests which verify buffer

* fix indent

* fix indent

* fix comments

* run all tests!

* use let instead of var
  • Loading branch information
stevenbrix authored Jan 25, 2024
1 parent 930217e commit f5c3a60
Show file tree
Hide file tree
Showing 45 changed files with 24,599 additions and 4,534 deletions.
30 changes: 30 additions & 0 deletions swiftwinrt/Resources/CWinRT/MemoryBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// The real MemoryBuffer.h doesn't provide C definitions, so we need to provide those here
// No pragma once, we might include this twice (once for the regular definitions, once for the workaround)

#include <objidl.h>

#ifndef _IMEMORYBUFFERBYTEACCESS_DEFINED
#define _IMEMORYBUFFERBYTEACCESS_DEFINED
typedef interface IMemoryBufferByteAccess IMemoryBufferByteAccess;
#endif

typedef struct IMemoryBufferByteAccessVtbl
{
BEGIN_INTERFACE

HRESULT (STDMETHODCALLTYPE* QueryInterface)(__RPC__in IMemoryBufferByteAccess* This,
REFIID riid,
_COM_Outptr_ void** ppvObject);
ULONG (STDMETHODCALLTYPE* AddRef)(__RPC__in IMemoryBufferByteAccess* This);
ULONG (STDMETHODCALLTYPE* Release)(__RPC__in IMemoryBufferByteAccess* This);
HRESULT (STDMETHODCALLTYPE* GetBuffer)(__RPC__in IMemoryBufferByteAccess* This,
BYTE** value,
_Out_ UINT32* capacity);

END_INTERFACE
} IMemoryBufferByteAccessVtbl;

interface IMemoryBufferByteAccess
{
CONST_VTBL struct IMemoryBufferByteAccessVtbl* lpVtbl;
};
32 changes: 32 additions & 0 deletions swiftwinrt/Resources/CWinRT/robuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// The real robuffer.h doesn't provide C definitions, so we need to provide those here

// No pragma once, we might include this twice (once for the regular definitions, once for the workaround)

#include <apiset.h>
#include <apisetcconv.h>
#include <objidl.h>

#ifndef _IBufferByteAccess_DEFINED
#define _IBufferByteAccess_DEFINED
typedef interface C_IBufferByteAccess C_IBufferByteAccess;
#endif /* _IBufferByteAccess_DEFINED */

typedef struct C_IBufferByteAccessVtbl
{
BEGIN_INTERFACE

HRESULT (STDMETHODCALLTYPE* QueryInterface)(__RPC__in C_IBufferByteAccess* This,
REFIID riid,
_COM_Outptr_ void** ppvObject);
ULONG (STDMETHODCALLTYPE* AddRef)(__RPC__in C_IBufferByteAccess* This);
ULONG (STDMETHODCALLTYPE* Release)(__RPC__in C_IBufferByteAccess* This);
HRESULT (STDMETHODCALLTYPE* Buffer)(__RPC__in C_IBufferByteAccess* This,
BYTE** value);

END_INTERFACE
} C_IBufferByteAccessVtbl;

interface C_IBufferByteAccess
{
CONST_VTBL struct C_IBufferByteAccessVtbl* lpVtbl;
};
64 changes: 64 additions & 0 deletions swiftwinrt/Resources/Support/IBufferByteAccess.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import C_BINDINGS_MODULE
import Foundation

/// IBufferByteAccess provides direct access to the underlying bytes of an IBuffer.
/// This buffer is only valid for the lifetime of the IBuffer. For a read-only representation
/// of the buffer, access the bytes through the Data property of the IBuffer.
/// [Open Microsoft Documentation](https://learn.microsoft.com/en-us/windows/win32/api/robuffer/ns-robuffer-ibufferbyteaccess)
public protocol IBufferByteAccess: WinRTInterface {
var buffer: UnsafeMutablePointer<UInt8>? { get throws }
}

public typealias AnyIBufferByteAccess = any IBufferByteAccess

fileprivate let IID_IBufferByteAccess = IID(Data1: 0x905A0FEF, Data2: 0xBC53, Data3: 0x11DF, Data4: ( 0x8C, 0x49, 0x00, 0x1E, 0x4F, 0xC6, 0x86, 0xDA )) // 905a0fef-bc53-11df-8c49-001e4fc686da

extension __ABI_ {
public class IBufferByteAccess: IUnknown {
override public class var IID: IID { IID_IBufferByteAccess}

public func Buffer() throws -> UnsafeMutablePointer<UInt8>? {
var buffer: UnsafeMutablePointer<UInt8>?
try perform(as: C_BINDINGS_MODULE.C_IBufferByteAccess.self) { pThis in
try CHECKED(pThis.pointee.lpVtbl.pointee.Buffer(pThis, &buffer))
}
return buffer
}
}

public typealias IBufferByteAccessWrapper = InterfaceWrapperBase<IBufferByteAccessBridge>
}

public enum IBufferByteAccessBridge: AbiInterfaceBridge {
public static func makeAbi() -> CABI {
return CABI(lpVtbl: &IBufferByteAccessVTable)
}

public static func from(abi: ComPtr<CABI>?) -> SwiftProjection? {
// This code path is not actually reachable since IBufferByteAccess is not a WinRT interface.
// It is a COM interface which is implemented by any object which implements the IBuffer interface.
// And the IBufferImpl object will correctly have the implementation of this interface, so this isn't needed
assertionFailure("IBufferByteAccessBridge.from not implemented")
return nil
}

public typealias CABI = C_BINDINGS_MODULE.C_IBufferByteAccess
public typealias SwiftABI = __ABI_.IBufferByteAccess
public typealias SwiftProjection = AnyIBufferByteAccess
}

private var IBufferByteAccessVTable: C_BINDINGS_MODULE.C_IBufferByteAccessVtbl = .init(
QueryInterface: { __ABI_.IBufferByteAccessWrapper.queryInterface($0, $1, $2) },
AddRef: { __ABI_.IBufferByteAccessWrapper.addRef($0) },
Release: { __ABI_.IBufferByteAccessWrapper.release($0) },
Buffer: { __ABI_.IBufferByteAccessWrapper.buffer($0, $1) }
)

extension __ABI_.IBufferByteAccessWrapper {
fileprivate static func buffer(_ this: UnsafeMutablePointer<C_BINDINGS_MODULE.C_IBufferByteAccess>?, _ buffer: UnsafeMutablePointer<UnsafeMutablePointer<UInt8>?>?) -> HRESULT {
guard let swiftObj = __ABI_.IBufferByteAccessWrapper.tryUnwrapFrom(raw: this) else { return E_INVALIDARG }
guard let buffer else { return E_INVALIDARG }
buffer.pointee = try? swiftObj.buffer
return S_OK
}
}
74 changes: 74 additions & 0 deletions swiftwinrt/Resources/Support/IMemoryBufferByteAccess.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import C_BINDINGS_MODULE
import Foundation

/// IMemoryBufferByteAccess provides direct access to the underlying bytes of an IMemoryBuffer.
/// This buffer is only valid for the lifetime of the buffer. For a read-only representation
/// of the buffer, access the bytes through the Data property of the IBuffer.
/// [Open Microsoft Documentation](https://learn.microsoft.com/en-us/previous-versions/mt297505(v=vs.85))
public protocol IMemoryBufferByteAccess: WinRTInterface {
var buffer: UnsafeMutableBufferPointer<UInt8>? { get throws }
}

public typealias AnyIMemoryBufferByteAccess = any IMemoryBufferByteAccess
fileprivate let IID_IMemoryBufferByteAccess = IID(Data1: 0x5B0D3235, Data2: 0x4DBA, Data3: 0x4D44, Data4: ( 0x86, 0x5E, 0x8F, 0x1D, 0x0E, 0x4F, 0xD0, 0x4D )) // 5b0d3235-4dba-4d44-865e-8f1d0e4fd04d

extension __ABI_ {
public class IMemoryBufferByteAccess: IUnknown {
override public class var IID: IID { IID_IMemoryBufferByteAccess}

public func Buffer() throws -> UnsafeMutableBufferPointer<UInt8>? {
var capacity: UInt32 = 0
let ptr = try GetBuffer(&capacity)
return UnsafeMutableBufferPointer(start: ptr, count: Int(capacity))
}

fileprivate func GetBuffer(_ capacity: inout UInt32) throws -> UnsafeMutablePointer<UInt8>? {
var buffer: UnsafeMutablePointer<UInt8>?
try perform(as: C_BINDINGS_MODULE.IMemoryBufferByteAccess.self) { pThis in
try CHECKED(pThis.pointee.lpVtbl.pointee.GetBuffer(pThis, &buffer, &capacity))
}
return buffer
}
}

public typealias IMemoryBufferByteAccessWrapper = InterfaceWrapperBase<IMemoryBufferByteAccessBridge>
}

public enum IMemoryBufferByteAccessBridge: AbiInterfaceBridge {
public static func makeAbi() -> CABI {
return CABI(lpVtbl: &IMemoryBufferByteAccessVTable)
}

public static func from(abi: ComPtr<CABI>?) -> SwiftProjection? {
// This code path is not actually reachable since IMemoryBufferByteAccess is not a WinRT interface.
// It is a COM interface which is implemented by any object which implements the IMemoryBufferReference interface.
// And the IMemoryBufferReferenceImpl object will correctly have the implementation of this interface, so this isn't needed
assertionFailure("IMemoryBufferByteAccessBridge.from not implemented")
return nil
}

public typealias CABI = C_BINDINGS_MODULE.IMemoryBufferByteAccess
public typealias SwiftABI = __ABI_.IMemoryBufferByteAccess
public typealias SwiftProjection = AnyIMemoryBufferByteAccess
}

fileprivate var IMemoryBufferByteAccessVTable: C_BINDINGS_MODULE.IMemoryBufferByteAccessVtbl = .init(
QueryInterface: { __ABI_.IMemoryBufferByteAccessWrapper.queryInterface($0, $1, $2) },
AddRef: { __ABI_.IMemoryBufferByteAccessWrapper.addRef($0) },
Release: { __ABI_.IMemoryBufferByteAccessWrapper.release($0) },
GetBuffer: { __ABI_.IMemoryBufferByteAccessWrapper.getBuffer($0, $1, $2) }
)

extension __ABI_.IMemoryBufferByteAccessWrapper {
fileprivate static func getBuffer(_ this: UnsafeMutablePointer<C_BINDINGS_MODULE.IMemoryBufferByteAccess>?, _ buffer: UnsafeMutablePointer<UnsafeMutablePointer<UInt8>?>?, _ count: UnsafeMutablePointer<UInt32>?) -> HRESULT {
guard let swiftObj = __ABI_.IMemoryBufferByteAccessWrapper.tryUnwrapFrom(raw: this) else { return E_INVALIDARG }
guard let buffer, let count else { return E_INVALIDARG }
count.pointee = 0
buffer.pointee = nil

guard let swiftBuffer = try? swiftObj.buffer else { return E_FAIL }
count.pointee = UInt32(swiftBuffer.count)
buffer.pointee = swiftBuffer.baseAddress
return S_OK
}
}
3 changes: 2 additions & 1 deletion swiftwinrt/abi_writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,8 @@ namespace swiftwinrt
#pragma clang diagnostic ignored "-Wmicrosoft-enum-forward-reference"
#include "CppInteropWorkaround.h" // TODO(WIN-860): Remove workaround once C++ interop issues with WinSDK.GUID are fixed.
#include "MemoryBuffer.h" // IMemoryBufferByteAccess (C definition)
#include "robuffer.h" // IBufferByteAccess (C definition)
)");
for (auto& [ns, members] : namespaces)
{
Expand Down
40 changes: 40 additions & 0 deletions swiftwinrt/code_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,18 @@ bind_bridge_fullname(type));
};

static std::string modifier_for(typedef_base const& type_definition, interface_info const& iface, member_type member_type = member_type::property_or_method);
static void write_bufferbyteaccess(writer& w, interface_info const& info, system_type const& type, typedef_base const& type_definition)
{
auto bufferType = type.swift_type_name() == "IBufferByteAccess" ? "UnsafeMutablePointer" : "UnsafeMutableBufferPointer";
w.write(R"(%var buffer: %<UInt8>? {
get throws {
let bufferByteAccess: %.__ABI_.% = try %.QueryInterface()
return try bufferByteAccess.Buffer()
}
}
)", modifier_for(type_definition, info), bufferType, w.support, type.swift_type_name(), get_swift_name(info));
}

static void write_interface_impl_members(writer& w, interface_info const& info, typedef_base const& type_definition)
{
w.add_depends(*info.type);
Expand Down Expand Up @@ -1050,6 +1062,13 @@ bind_bridge_fullname(type));
write_class_impl_event(w, event, info, type_definition);
}
}
else if (auto systemType = dynamic_cast<const system_type*>(info.type))
{
if (systemType->swift_type_name() == "IBufferByteAccess" || systemType->swift_type_name() == "IMemoryBufferByteAccess")
{
write_bufferbyteaccess(w, info, *systemType, type_definition);
}
}
else
{
assert(!"Unexpected interface type.");
Expand Down Expand Up @@ -1370,6 +1389,27 @@ vtable);
w.write(" }\n");
w.write(" }\n");
w.write("}\n");

if (type.swift_full_name() == "Windows.Storage.Streams.IBuffer")
{
w.write(R"(extension IBuffer {
public var data: Data {
guard let buffer = try? buffer else { return Data() }
return Data(bytesNoCopy: buffer, count: Int(length), deallocator: .none)
}
}
)");
}
else if (type.swift_full_name() == "Windows.Foundation.IMemoryBufferReference")
{
w.write(R"(extension IMemoryBufferReference {
public var data: Data {
guard let buffer = try? buffer, let ptr = buffer.baseAddress else { return Data() }
return Data(bytesNoCopy: ptr, count: buffer.count, deallocator: .none)
}
}
)");
}
}
// Declare a short form for the existential version of the type, e.g. AnyClosable for "any IClosable"
w.write("public typealias Any% = any %\n\n", type, type);
Expand Down
6 changes: 6 additions & 0 deletions swiftwinrt/file_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ namespace swiftwinrt
auto restrictederrorinfo_h_template = find_resource(RESOURCE_TYPE_OTHER_FILE_STR, RESOURCE_NAME_CWINRT_RESTRICTEDERRORINFO_H_STR);
fill_template_placeholders_to_file(restrictederrorinfo_h_template, dir_path / "include" / "RestrictedErrorInfo.h");

auto robuffer_h_template = find_resource(RESOURCE_TYPE_OTHER_FILE_STR, RESOURCE_NAME_CWINRT_ROBUFFER_H_STR);
fill_template_placeholders_to_file(robuffer_h_template, dir_path / "include" / "robuffer.h");

auto memorybuffer_h_template = find_resource(RESOURCE_TYPE_OTHER_FILE_STR, RESOURCE_NAME_CWINRT_MEMORYBUFFER_H_STR);
fill_template_placeholders_to_file(memorybuffer_h_template, dir_path / "include" / "MemoryBuffer.h");

if (settings.has_project_type(project_type::spm))
{
auto package_template = find_resource(RESOURCE_TYPE_OTHER_FILE_STR, RESOURCE_NAME_CWINRT_PACKAGE_SWIFT_STR);
Expand Down
15 changes: 15 additions & 0 deletions swiftwinrt/metadata_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,25 @@ void metadata_cache::get_interfaces_impl(init_state& state, writer& w, get_inter

process_contract_dependencies(*state.target, impl);
get_interfaces_impl(state, w, result, info.defaulted, info.overridable, base, typeBase->type().InterfaceImpl());
try_insert_buffer_byte_access(typeBase->type(), result, info.defaulted);
}

insert_or_assign(result, name, std::move(info));
}
};

void metadata_cache::try_insert_buffer_byte_access(winmd::reader::TypeDef const& type, get_interfaces_t& result, bool defaulted = false)
{
if (type.TypeNamespace() == "Windows.Foundation" && type.TypeName() == "IMemoryBufferReference")
{
insert_or_assign(result, "IMemoryBufferByteAccess", { &system_type::from_name("IMemoryBufferByteAccess"), false, defaulted });
}
else if (type.TypeNamespace() == "Windows.Storage.Streams" && type.TypeName() == "IBuffer")
{
insert_or_assign(result, "IBufferByteAccess", { &system_type::from_name("IBufferByteAccess"), false, defaulted });
}
}

metadata_cache::get_interfaces_t metadata_cache::get_interfaces(init_state& state, TypeDef const& type)
{
get_interfaces_t result;
Expand All @@ -285,6 +298,8 @@ metadata_cache::get_interfaces_t metadata_cache::get_interfaces(init_state& stat
get_interfaces_impl(state,w, result, false, false, true, base.InterfaceImpl());
}

try_insert_buffer_byte_access(type, result);

if (!has_fastabi(type))
{
return result;
Expand Down
4 changes: 3 additions & 1 deletion swiftwinrt/metadata_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ namespace swiftwinrt
type_cache compile_namespaces(std::vector<std::string_view> const& targetNamespaces, metadata_filter const& f);

std::set<std::string_view> get_dependent_namespaces(std::vector<std::string_view> const& targetNamespaces, metadata_filter const& f);

bool has_namespace(std::string_view typeNamespace) const
{
return m_typeTable.find(typeNamespace) != m_typeTable.end();
Expand Down Expand Up @@ -171,5 +171,7 @@ namespace swiftwinrt
std::map<std::string, attributed_type> get_attributed_types(winmd::reader::TypeDef const& type) const;

std::map<std::string_view, std::map<std::string_view, metadata_type const&>> m_typeTable;

void try_insert_buffer_byte_access(winmd::reader::TypeDef const& type, get_interfaces_t& result, bool defaulted);
};
}
6 changes: 6 additions & 0 deletions swiftwinrt/resources.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
#define RESOURCE_NAME_CWINRT_RESTRICTEDERRORINFO_H CWINRT_RESTRICTEDERRORINFO
#define RESOURCE_NAME_CWINRT_RESTRICTEDERRORINFO_H_STR "CWINRT_RESTRICTEDERRORINFO"

#define RESOURCE_NAME_CWINRT_ROBUFFER_H CWINRT_ROBUFFER
#define RESOURCE_NAME_CWINRT_ROBUFFER_H_STR "CWINRT_ROBUFFER"

#define RESOURCE_NAME_CWINRT_MEMORYBUFFER_H CWINRT_MEMORYBUFFER
#define RESOURCE_NAME_CWINRT_MEMORYBUFFER_H_STR "CWINRT_MEMORYBUFFER"

#ifndef RC_INVOKED

#include <span>
Expand Down
5 changes: 5 additions & 0 deletions swiftwinrt/resources.rc
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ Error RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\Error.swift"
ErrorHandling RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\ErrorHandling.swift"
GUID RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\GUID.swift"
HString RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\HString.swift"
IBufferByteAccess RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\IBufferByteAccess.swift"
IInspectable RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\IInspectable.swift"
IInspectable+Swift RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\IInspectable+Swift.swift"
IMap+Swift RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\IMap+Swift.swift"
IMemoryBufferByteAccess RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\IMemoryBufferByteAccess.swift"
IUnknown RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\IUnknown.swift"
IUnknown+Swift RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\IUnknown+Swift.swift"
IUnknownRef RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\IUnknownRef.swift"
Expand All @@ -41,3 +43,6 @@ RESOURCE_NAME_CWINRT_IVECTORCHANGEDEVENTARGS_H RESOURCE_TYPE_OTHER_FILE "Resourc
RESOURCE_NAME_CWINRT_RESTRICTEDERRORINFO_H RESOURCE_TYPE_OTHER_FILE "Resources\\CWinRT\\RestrictedErrorInfo.h"
RESOURCE_NAME_CWINRT_SHIM_C RESOURCE_TYPE_OTHER_FILE "Resources\\CWinRT\\shim.c"
RESOURCE_NAME_CWINRT_PACKAGE_SWIFT RESOURCE_TYPE_OTHER_FILE "Resources\\CWinRT\\Package.swift"

RESOURCE_NAME_CWINRT_ROBUFFER_H RESOURCE_TYPE_OTHER_FILE "Resources\\CWinRT\\robuffer.h"
RESOURCE_NAME_CWINRT_MEMORYBUFFER_H RESOURCE_TYPE_OTHER_FILE "Resources\\CWinRT\\MemoryBuffer.h"
10 changes: 10 additions & 0 deletions swiftwinrt/types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,16 @@ namespace swiftwinrt
static system_type const guid_type{ "Foundation"sv, "UUID"sv, "Foundation.UUID"sv, "GUID"sv, "g16"sv, param_category::guid_type };
return guid_type;
}
else if (typeName == "IBufferByteAccess")
{
static system_type const ibufferbyteaccess_type{ ""sv, typeName, typeName, "C_IBufferByteAccess"sv, "{905a0fef-bc53-11df-8c49-001e4fc686da}"sv, param_category::object_type };
return ibufferbyteaccess_type;
}
else if (typeName == "IMemoryBufferByteAccess")
{
static system_type const imemorybufferbyte_type{ ""sv, typeName, typeName, "IMemoryBufferByteAccess"sv, "{5b0d3235-4dba-4d44-865e-8f1d0e4fd04d}"sv, param_category::object_type };
return imemorybufferbyte_type;
}

XLANG_ASSERT(false);
swiftwinrt::throw_invalid("Unknown type '", typeName, "' in System namespace");
Expand Down
Loading

0 comments on commit f5c3a60

Please sign in to comment.