#include #include #include #include #include template struct reference { using type = T&; }; template <> struct reference { using type = void; }; struct unit {}; template struct tracked_pointer : thrust::iterator_facade, T, thrust::host_system_tag, thrust::random_access_traversal_tag, typename reference::type, std::ptrdiff_t> { using raw_pointer = T*; std::size_t id{}; std::size_t size{}; std::size_t alignment{}; std::size_t offset{}; void* ptr{nullptr}; _CCCL_HOST_DEVICE explicit tracked_pointer(T* ptr = nullptr) : ptr(ptr) {} ~tracked_pointer() = default; template operator tracked_pointer() const { tracked_pointer ret; ret.id = id; ret.size = size; ret.alignment = alignment; ret.offset = offset; ret.ptr = ptr; return ret; } _CCCL_HOST_DEVICE std::ptrdiff_t distance_to(const tracked_pointer& other) const { return static_cast(other.ptr) - static_cast(ptr); } _CCCL_HOST_DEVICE T* get() const { return static_cast(ptr); } // globally qualified, because MSVC somehow prefers the name from the dependent base // of this class over the `reference` template that's visible in the global namespace of this file... _CCCL_HOST_DEVICE typename ::reference::type dereference() const { return *get(); } _CCCL_HOST_DEVICE void increment() { advance(1); } _CCCL_HOST_DEVICE void decrement() { advance(-1); } _CCCL_HOST_DEVICE void advance(std::ptrdiff_t diff) { ptr = get() + diff; offset += diff * sizeof(T); } _CCCL_HOST_DEVICE bool equal(const tracked_pointer& other) const { return id == other.id && size == other.size && alignment == other.alignment && offset == other.offset && ptr == other.ptr; } }; class tracked_resource final : public thrust::mr::memory_resource> { public: tracked_resource() = default; ~tracked_resource() override // NOLINT(bugprone-exception-escape) { ASSERT_EQUAL(id_to_allocate, 0u); ASSERT_EQUAL(id_to_deallocate, 0u); } tracked_pointer do_allocate(std::size_t n, std::size_t alignment = THRUST_MR_DEFAULT_ALIGNMENT) override { ASSERT_EQUAL(id_to_allocate || id_to_allocate == -1u, true); void* raw = upstream.do_allocate(n, alignment); tracked_pointer ret(raw); ret.id = id_to_allocate; ret.size = n; ret.alignment = alignment; if (id_to_allocate != -1u) { id_to_allocate = 0; } return ret; } void do_deallocate(tracked_pointer p, std::size_t n, std::size_t alignment = THRUST_MR_DEFAULT_ALIGNMENT) override { ASSERT_GEQUAL(p.size, n); ASSERT_GEQUAL(p.alignment, alignment); if (id_to_deallocate != 0) { ASSERT_EQUAL(p.id, id_to_deallocate); id_to_deallocate = 0; } upstream.do_deallocate(p.ptr, n, alignment); } std::size_t id_to_allocate{}; std::size_t id_to_deallocate{}; private: thrust::mr::new_delete_resource upstream; }; template