1+ #ifndef CPP_MVS_FLEXIBLE_ARRAY_UNCHECKED_HPP
2+ #define CPP_MVS_FLEXIBLE_ARRAY_UNCHECKED_HPP
3+
4+ #include " library.h"
5+
6+ // / A buffer of header and elements stored in a contiguous region of memory, whose size is determined at
7+ // / instance creation.
8+ // /
9+ // / Note: After initialization, the header's destruction is managed by the FlexibleArray.
10+ // /
11+ // / The FlexibleArray stores its elements out of line, so it is **movable** but **not copyable**.
12+ // /
13+ // / Warning: The destructor of `FlexibleArray` does not destroy the elements that may
14+ // / be stored in its payload. You must ensure that they are properly destroyed before destroying this object.
15+ // / Similarly, the initialization of `FlexibleArray` doesn't start the lifetime of its elements, so users must
16+ // / use placement new or std::construct_at to create the object.
17+ template <TrailingElementCountProvider Header, typename Element>
18+ struct FlexibleArrayUnchecked
19+ {
20+ private:
21+ // / Storage containing Header, potential padding, then `capacity` number of elements.
22+ // /
23+ // / May be null in case of a moved-from object.
24+ UnsafeMutableRawPointer storage;
25+
26+ // / The offset of the start of the array from the start of the storage, given in bytes.
27+ [[nodiscard]] static constexpr auto elements_offset () noexcept -> Int
28+ {
29+ return align_up (sizeof (Header), alignof (Element));
30+ }
31+
32+ // / The total space required for the storage of `element_count` elements, given in bytes.
33+ // /
34+ // / Guaranteed to be a multiple of `Header`'s alignment.
35+ [[nodiscard]] static constexpr auto storage_size_for (const Int element_count) noexcept -> size_t
36+ {
37+ return align_up (static_cast <size_t >(elements_offset () + (sizeof (Element) * element_count)), alignof (Header));
38+ }
39+
40+ // / Constructs a flexible array by taking ownership of an existing storage.
41+ [[nodiscard]] constexpr explicit FlexibleArrayUnchecked (char * const owned_storage) noexcept : storage(owned_storage) {}
42+
43+ // / Returns the address of the first array element.
44+ // /
45+ // / Note: There may be no element at the returned address when `capacity() == 0`.
46+ // / Requires the object being in a valid, non-moved-from state.
47+ template <typename Self>
48+ [[nodiscard]] constexpr auto elements_start (this Self&& self) -> const_pointee_like<Self, Element*>
49+ {
50+ return reinterpret_cast <const_pointee_like<Self, Element*>>(self.storage + elements_offset ());
51+ }
52+
53+ public:
54+ // / Constructs a buffer with enough space to hold the header and `capacity` number of Elements.
55+ // /
56+ // / `init_header` must initialize the header by placement new/std::construct_at at the supplied memory address.
57+ [[nodiscard]] static constexpr auto with_header_initialized_by (Int const capacity,
58+ std::invocable<Header*> auto && init_header) noexcept
59+ -> FlexibleArrayUnchecked
60+ {
61+ auto * storage = static_cast <char *>(
62+ Detail::aligned_alloc (storage_size_for (capacity), std::max (alignof (Header), alignof (Element))));
63+ init_header (reinterpret_cast <Header*>(storage));
64+ return FlexibleArrayUnchecked{storage};
65+ }
66+
67+ // / Constructs a buffer with enough space to hold the header and `capacity` number of Elements.
68+ // /
69+ // / The given `header` is moved into the storage.
70+ [[nodiscard]] static constexpr auto with_header (Int const capacity, Header&& header) noexcept -> FlexibleArrayUnchecked
71+ requires(std::movable<Header>)
72+ {
73+ return with_header_initialized_by (capacity,
74+ [&](Header* place) { std::construct_at (place, std::move (header)); });
75+ }
76+
77+ // / Returns the address for the place of the `i`th element in the array.
78+ // /
79+ // / Requires `i` < `capacity()`, and the object being in a valid, non-moved-from state.
80+ template <typename Self>
81+ [[nodiscard]] constexpr auto element_address (this Self&& self, const Int i) noexcept
82+ -> const_pointee_like<Self, Element*>
83+ {
84+ return self.elements_start () + i;
85+ }
86+
87+ // / Returns the pointer to the header.
88+ // /
89+ // / Requires the FlexibleArray being in a valid, non-moved-from state.
90+ template <typename Self>
91+ [[nodiscard]] constexpr auto header (this Self&& self) noexcept -> const_pointee_like<Self, Header*>
92+ {
93+ return reinterpret_cast <const_pointee_like<Self, Header*>>(self.storage );
94+ }
95+
96+ // / Destroying the header unless the object is in a moved-from state.
97+ ~FlexibleArrayUnchecked ()
98+ {
99+ if (storage != nullptr )
100+ {
101+ std::destroy_at (header ());
102+ Detail::aligned_free (storage);
103+ }
104+ }
105+
106+ // / Extracts the storage out of the trailing array, handing out the ownership to the callee.
107+ // /
108+ // / The underlying storage won't be freed by this FlexibleArray.
109+ [[nodiscard]] constexpr auto leak_storage () -> UnsafeMutableRawPointer { return std::exchange (storage, nullptr ); }
110+
111+ // Not copyable
112+ FlexibleArrayUnchecked (const FlexibleArrayUnchecked& other) = delete ;
113+ FlexibleArrayUnchecked& operator =(const FlexibleArrayUnchecked& other) = delete ;
114+
115+ // / Move constructor
116+ FlexibleArrayUnchecked (FlexibleArrayUnchecked&& other) noexcept : storage(other.storage) { other.storage = nullptr ; }
117+ // / Move assignment operator
118+ FlexibleArrayUnchecked& operator =(FlexibleArrayUnchecked&& other) noexcept
119+ {
120+ // Moving to self is a no-op
121+ if (this == &other)
122+ {
123+ return *this ;
124+ }
125+ // Destroying the header unless the object was in a moved-from state.
126+ if (storage != nullptr )
127+ {
128+ std::destroy_at (header ());
129+ Detail::aligned_free (storage);
130+ }
131+ // Taking ownership of the other object's storage, marking the other object as moved-from.
132+ storage = other.storage ;
133+ other.storage = nullptr ;
134+ return *this ;
135+ }
136+
137+ // / Swaps the underlying storage of `a` and `b`.
138+ friend void swap (FlexibleArrayUnchecked& a, FlexibleArrayUnchecked& b) noexcept { std::swap (a.storage , b.storage ); }
139+
140+ // / Projects a stack-allocated temporary
141+ template <std::invocable<FlexibleArrayUnchecked&> F>
142+ static constexpr auto project_temporary (Int element_count, F consumer) -> std::invoke_result_t<F, FlexibleArrayUnchecked&>
143+ {
144+ auto storage_size = FlexibleArrayUnchecked::storage_size_for (element_count);
145+ char * storage = aligned_alloca (storage_size, alignof (Header));
146+
147+ FlexibleArrayUnchecked flexible_array{storage};
148+ auto result = consumer (flexible_array);
149+
150+ std::destroy_at (reinterpret_cast <Header*>(flexible_array.leak_storage ()));
151+
152+ return result;
153+ }
154+ };
155+
156+ #endif // CPP_MVS_FLEXIBLE_ARRAY_UNCHECKED_HPP
0 commit comments