test/common/buffer.hpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // | ||
| 2 | // SPDX-FileCopyrightText: Copyright 2025 Arm Limited and/or its affiliates <open-source-office@arm.com> | ||
| 3 | // | ||
| 4 | // SPDX-License-Identifier: Apache-2.0 | ||
| 5 | // | ||
| 6 | |||
| 7 | #pragma once | ||
| 8 | |||
| 9 | #include <cstddef> | ||
| 10 | #include <cstdint> | ||
| 11 | #include <functional> | ||
| 12 | #include <memory> | ||
| 13 | |||
| 14 | #include "test/common/span.hpp" | ||
| 15 | |||
| 16 | namespace kai::test { | ||
| 17 | |||
| 18 | /// Buffer is a high-level abstraction for a block of memory. | ||
| 19 | /// | ||
| 20 | /// The class performs dynamic memory allocation and management in an opaque manner. The underlying memory resource can | ||
| 21 | /// be requested using the familiar @ref Buffer::data() method and interacted with using @ref | ||
| 22 | /// kai::test::read_array<T>() and @ref kai::test::write_array<T>() utilities. | ||
| 23 | /// | ||
| 24 | /// Buffer comes with protection mechanisms defined by @ref BufferProtectionPolicy. These are enabled by setting the | ||
| 25 | /// KAI_TEST_BUFFER_POLICY environment variable, for example: | ||
| 26 | /// KAI_TEST_BUFFER_POLICY=PROTECT_UNDERFLOW to enable @ref BufferProtectionPolicy::ProtectUnderflow. | ||
| 27 | /// KAI_TEST_BUFFER_POLICY=PROTECT_OVERFLOW to enable @ref BufferProtectionPolicy::ProtectOverflow. | ||
| 28 | /// | ||
| 29 | class Buffer { | ||
| 30 | // Handle to the underlying memory resource and its deleter | ||
| 31 | using handle = std::unique_ptr<void, std::function<void(void*)>>; | ||
| 32 | |||
| 33 | public: | ||
| 34 | 929553 | Buffer() = default; | |
| 35 | explicit Buffer(size_t size); | ||
| 36 | Buffer(size_t size, uint8_t init_value); | ||
| 37 | |||
| 38 | Buffer(const Buffer& other) = delete; | ||
| 39 | 948549 | Buffer(Buffer&& other) noexcept = default; | |
| 40 | Buffer& operator=(const Buffer& other) = delete; | ||
| 41 | 293823 | Buffer& operator=(Buffer&& other) noexcept = default; | |
| 42 | |||
| 43 | 2950667 | ~Buffer() = default; | |
| 44 | |||
| 45 | /// Gets the base memory address of the user buffer. | ||
| 46 | /// | ||
| 47 | /// @return Base memory address of the user buffer. | ||
| 48 | 1063184133 | [[nodiscard]] std::byte* data() const { | |
| 49 | 1063184133 | return static_cast<std::byte*>(m_buffer.get()) + m_user_buffer_offset; | |
| 50 | } | ||
| 51 | |||
| 52 | /// Gets a view of the data. | ||
| 53 | 11270 | operator Span<const std::byte>() const { | |
| 54 | 11270 | return {data(), size()}; | |
| 55 | } | ||
| 56 | |||
| 57 | /// Gets a view of the data. | ||
| 58 | 2656047 | operator Span<std::byte>() { | |
| 59 | 2656047 | return {data(), size()}; | |
| 60 | } | ||
| 61 | |||
| 62 | /// Gets a view of the data. | ||
| 63 | [[nodiscard]] Span<const std::byte> view() const { | ||
| 64 | return {data(), size()}; | ||
| 65 | } | ||
| 66 | |||
| 67 | /// Gets a view of the data. | ||
| 68 | 5040386 | [[nodiscard]] Span<std::byte> view() { | |
| 69 | 5040386 | return {data(), size()}; | |
| 70 | } | ||
| 71 | |||
| 72 | /// Gets the size of the user buffer. | ||
| 73 | /// | ||
| 74 | /// Depending on the @ref BufferProtectionPolicy policy enabled, the actual size of memory allocated may be larger. | ||
| 75 | /// However, this function guarantees to always provide the size of the user buffer only. | ||
| 76 | /// | ||
| 77 | /// @return Size of the user buffer in bytes. | ||
| 78 | 11906396 | [[nodiscard]] size_t size() const { | |
| 79 | 11906396 | return m_user_buffer_size; | |
| 80 | } | ||
| 81 | |||
| 82 | static constexpr const char* buffer_policy_env_name = "KAI_TEST_BUFFER_POLICY"; | ||
| 83 | |||
| 84 | private: | ||
| 85 | /// Buffer can be protected with one of the following protection policies: | ||
| 86 | /// - @ref BufferProtectionPolicy::None No protection mechanisms are enabled. | ||
| 87 | /// - @ref BufferProtectionPolicy::ProtectUnderflow Memory equal to the size of the user buffer rounded to the | ||
| 88 | /// nearest whole page plus adjacent guard pages is allocated, | ||
| 89 | /// and the user buffer is aligned to the end of the head guard | ||
| 90 | /// page thus detecting whenever a buffer underflow occurs. | ||
| 91 | /// - @ref BufferProtectionPolicy::ProtectOverflow Same as above, but now the edge of the user buffer is aligned | ||
| 92 | /// to the start of the tail guard page thus detecting whenever a | ||
| 93 | /// buffer overflow occurs. | ||
| 94 | enum class BufferProtectionPolicy : uint8_t { | ||
| 95 | None = 0, | ||
| 96 | ProtectUnderflow = 1, | ||
| 97 | ProtectOverflow = 2, | ||
| 98 | }; | ||
| 99 | |||
| 100 | /// Naively allocate memory. | ||
| 101 | void allocate(); | ||
| 102 | |||
| 103 | #if defined(__linux__) || defined(__APPLE__) | ||
| 104 | /// Allocate memory with adjacent guard pages. | ||
| 105 | void allocate_with_guard_pages(); | ||
| 106 | #endif // defined(__linux__) || defined(__APPLE__) | ||
| 107 | |||
| 108 | 350105 | handle m_buffer = nullptr; | |
| 109 | |||
| 110 | 350105 | size_t m_user_buffer_size = 0; | |
| 111 | 350105 | size_t m_user_buffer_offset = 0; | |
| 112 | |||
| 113 | 350105 | BufferProtectionPolicy m_protection_policy = BufferProtectionPolicy::None; | |
| 114 | }; | ||
| 115 | |||
| 116 | } // namespace kai::test | ||
| 117 |