ipc: Allow all trivially copyable objects to be passed directly into WriteBuffer (#4465)
* ipc: Allow all trivially copyable objects to be passed directly into WriteBuffer With the support of C++20, we can use concepts to deduce if a type is an STL container or not. * More agressive concept for stl containers * Add -fconcepts * Move to common namespace * Add Common::IsBaseOf
This commit is contained in:
		@@ -60,6 +60,7 @@ else()
 | 
			
		||||
        -Wmissing-declarations
 | 
			
		||||
        -Wno-attributes
 | 
			
		||||
        -Wno-unused-parameter
 | 
			
		||||
        -fconcepts
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if (ARCHITECTURE_x86_64)
 | 
			
		||||
 
 | 
			
		||||
@@ -110,6 +110,7 @@ add_library(common STATIC
 | 
			
		||||
    common_funcs.h
 | 
			
		||||
    common_paths.h
 | 
			
		||||
    common_types.h
 | 
			
		||||
    concepts.h
 | 
			
		||||
    dynamic_library.cpp
 | 
			
		||||
    dynamic_library.h
 | 
			
		||||
    fiber.cpp
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										32
									
								
								src/common/concepts.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/common/concepts.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
// Copyright 2020 yuzu emulator team
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
namespace Common {
 | 
			
		||||
 | 
			
		||||
#include <type_traits>
 | 
			
		||||
 | 
			
		||||
// Check if type is like an STL container
 | 
			
		||||
template <typename T>
 | 
			
		||||
concept IsSTLContainer = requires(T t) {
 | 
			
		||||
    typename T::value_type;
 | 
			
		||||
    typename T::iterator;
 | 
			
		||||
    typename T::const_iterator;
 | 
			
		||||
    // TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it.
 | 
			
		||||
    t.begin();
 | 
			
		||||
    t.end();
 | 
			
		||||
    t.cbegin();
 | 
			
		||||
    t.cend();
 | 
			
		||||
    t.data();
 | 
			
		||||
    t.size();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Check if type T is derived from T2
 | 
			
		||||
template <typename T, typename T2>
 | 
			
		||||
concept IsBaseOf = requires {
 | 
			
		||||
    std::is_base_of_v<T, T2>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Common
 | 
			
		||||
@@ -13,6 +13,7 @@
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <boost/container/small_vector.hpp>
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/concepts.h"
 | 
			
		||||
#include "common/swap.h"
 | 
			
		||||
#include "core/hle/ipc.h"
 | 
			
		||||
#include "core/hle/kernel/object.h"
 | 
			
		||||
@@ -193,23 +194,24 @@ public:
 | 
			
		||||
 | 
			
		||||
    /* Helper function to write a buffer using the appropriate buffer descriptor
 | 
			
		||||
     *
 | 
			
		||||
     * @tparam ContiguousContainer an arbitrary container that satisfies the
 | 
			
		||||
     *         ContiguousContainer concept in the C++ standard library.
 | 
			
		||||
     * @tparam T an arbitrary container that satisfies the
 | 
			
		||||
     *         ContiguousContainer concept in the C++ standard library or a trivially copyable type.
 | 
			
		||||
     *
 | 
			
		||||
     * @param container    The container to write the data of into a buffer.
 | 
			
		||||
     * @param data         The container/data to write into a buffer.
 | 
			
		||||
     * @param buffer_index The buffer in particular to write to.
 | 
			
		||||
     */
 | 
			
		||||
    template <typename ContiguousContainer,
 | 
			
		||||
              typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>>
 | 
			
		||||
    std::size_t WriteBuffer(const ContiguousContainer& container,
 | 
			
		||||
                            std::size_t buffer_index = 0) const {
 | 
			
		||||
        using ContiguousType = typename ContiguousContainer::value_type;
 | 
			
		||||
 | 
			
		||||
        static_assert(std::is_trivially_copyable_v<ContiguousType>,
 | 
			
		||||
                      "Container to WriteBuffer must contain trivially copyable objects");
 | 
			
		||||
 | 
			
		||||
        return WriteBuffer(std::data(container), std::size(container) * sizeof(ContiguousType),
 | 
			
		||||
                           buffer_index);
 | 
			
		||||
    template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
 | 
			
		||||
    std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const {
 | 
			
		||||
        if constexpr (Common::IsSTLContainer<T>) {
 | 
			
		||||
            using ContiguousType = typename T::value_type;
 | 
			
		||||
            static_assert(std::is_trivially_copyable_v<ContiguousType>,
 | 
			
		||||
                          "Container to WriteBuffer must contain trivially copyable objects");
 | 
			
		||||
            return WriteBuffer(std::data(data), std::size(data) * sizeof(ContiguousType),
 | 
			
		||||
                               buffer_index);
 | 
			
		||||
        } else {
 | 
			
		||||
            static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
 | 
			
		||||
            return WriteBuffer(&data, sizeof(T), buffer_index);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Helper function to get the size of the input buffer
 | 
			
		||||
 
 | 
			
		||||
@@ -286,9 +286,7 @@ protected:
 | 
			
		||||
        ProfileBase profile_base{};
 | 
			
		||||
        ProfileData data{};
 | 
			
		||||
        if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
 | 
			
		||||
            std::array<u8, sizeof(ProfileData)> raw_data;
 | 
			
		||||
            std::memcpy(raw_data.data(), &data, sizeof(ProfileData));
 | 
			
		||||
            ctx.WriteBuffer(raw_data);
 | 
			
		||||
            ctx.WriteBuffer(data);
 | 
			
		||||
            IPC::ResponseBuilder rb{ctx, 16};
 | 
			
		||||
            rb.Push(RESULT_SUCCESS);
 | 
			
		||||
            rb.PushRaw(profile_base);
 | 
			
		||||
@@ -333,7 +331,7 @@ protected:
 | 
			
		||||
        std::vector<u8> buffer(size);
 | 
			
		||||
        image.ReadBytes(buffer.data(), buffer.size());
 | 
			
		||||
 | 
			
		||||
        ctx.WriteBuffer(buffer.data(), buffer.size());
 | 
			
		||||
        ctx.WriteBuffer(buffer);
 | 
			
		||||
        rb.Push<u32>(size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -92,7 +92,7 @@ private:
 | 
			
		||||
        if (performance) {
 | 
			
		||||
            rb.Push<u64>(*performance);
 | 
			
		||||
        }
 | 
			
		||||
        ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
 | 
			
		||||
        ctx.WriteBuffer(samples);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
 | 
			
		||||
 
 | 
			
		||||
@@ -112,7 +112,7 @@ private:
 | 
			
		||||
    void GetImpl(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
        LOG_DEBUG(Service_BCAT, "called");
 | 
			
		||||
 | 
			
		||||
        ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl));
 | 
			
		||||
        ctx.WriteBuffer(impl);
 | 
			
		||||
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
 
 | 
			
		||||
@@ -160,7 +160,7 @@ private:
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ctx.WriteBuffer(key.data(), key.size());
 | 
			
		||||
        ctx.WriteBuffer(key);
 | 
			
		||||
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
 
 | 
			
		||||
@@ -127,7 +127,7 @@ private:
 | 
			
		||||
        const u32 array_size = rp.Pop<u32>();
 | 
			
		||||
        LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
 | 
			
		||||
 | 
			
		||||
        ctx.WriteBuffer(&device_handle, sizeof(device_handle));
 | 
			
		||||
        ctx.WriteBuffer(device_handle);
 | 
			
		||||
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 3};
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
@@ -220,7 +220,7 @@ private:
 | 
			
		||||
 | 
			
		||||
        tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
 | 
			
		||||
        tag_info.tag_type = 2;
 | 
			
		||||
        ctx.WriteBuffer(&tag_info, sizeof(TagInfo));
 | 
			
		||||
        ctx.WriteBuffer(tag_info);
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -237,7 +237,7 @@ private:
 | 
			
		||||
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
        auto amiibo = nfp_interface.GetAmiiboBuffer();
 | 
			
		||||
        ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info));
 | 
			
		||||
        ctx.WriteBuffer(amiibo.model_info);
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -283,7 +283,7 @@ private:
 | 
			
		||||
 | 
			
		||||
        CommonInfo common_info{};
 | 
			
		||||
        common_info.application_area_size = 0;
 | 
			
		||||
        ctx.WriteBuffer(&common_info, sizeof(CommonInfo));
 | 
			
		||||
        ctx.WriteBuffer(common_info);
 | 
			
		||||
 | 
			
		||||
        IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
 
 | 
			
		||||
@@ -106,7 +106,7 @@ void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
 | 
			
		||||
    IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    ctx.WriteBuffer(&layout, sizeof(KeyboardLayout));
 | 
			
		||||
    ctx.WriteBuffer(layout);
 | 
			
		||||
}
 | 
			
		||||
} // Anonymous namespace
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -290,7 +290,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
 | 
			
		||||
    IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
 | 
			
		||||
    ctx.WriteBuffer(clock_snapshot);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
@@ -313,7 +313,7 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques
 | 
			
		||||
 | 
			
		||||
    IPC::ResponseBuilder rb{ctx, 2};
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
 | 
			
		||||
    ctx.WriteBuffer(clock_snapshot);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
 | 
			
		||||
 
 | 
			
		||||
@@ -142,7 +142,7 @@ void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    IPC::ResponseBuilder rb{ctx, 3};
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    rb.PushRaw<u32>(1); // Number of times we're returning
 | 
			
		||||
    ctx.WriteBuffer(&posix_time, sizeof(s64));
 | 
			
		||||
    ctx.WriteBuffer(posix_time);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
@@ -164,7 +164,7 @@ void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    IPC::ResponseBuilder rb{ctx, 3};
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    rb.PushRaw<u32>(1); // Number of times we're returning
 | 
			
		||||
    ctx.WriteBuffer(&posix_time, sizeof(s64));
 | 
			
		||||
    ctx.WriteBuffer(posix_time);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Service::Time
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user