Line data Source code
1 : //
2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/boostorg/json
9 : //
10 :
11 : #ifndef BOOST_JSON_MONOTONIC_RESOURCE_HPP
12 : #define BOOST_JSON_MONOTONIC_RESOURCE_HPP
13 :
14 : #include <boost/container/pmr/memory_resource.hpp>
15 : #include <boost/json/detail/config.hpp>
16 : #include <boost/json/storage_ptr.hpp>
17 : #include <cstddef>
18 : #include <utility>
19 :
20 : namespace boost {
21 : namespace json {
22 :
23 : #ifdef _MSC_VER
24 : #pragma warning(push)
25 : #pragma warning(disable: 4251) // class needs to have dll-interface to be used by clients of class
26 : #pragma warning(disable: 4275) // non dll-interface class used as base for dll-interface class
27 : #endif
28 :
29 : //----------------------------------------------------------
30 :
31 : /** A dynamically allocating resource with a trivial deallocate.
32 :
33 : This memory resource is a special-purpose resource that releases allocated
34 : memory only when the resource is destroyed (or when @ref release is
35 : called). It has a trivial deallocate function; that is, the metafunction
36 : @ref is_deallocate_trivial returns `true`.
37 :
38 : The resource can be constructed with an initial buffer. If there is no
39 : initial buffer, or if the buffer is exhausted, subsequent dynamic
40 : allocations are made from the system heap. The size of buffers obtained in
41 : this fashion follow a geometric progression.
42 :
43 : The purpose of this resource is to optimize the use case for performing
44 : many allocations, followed by deallocating everything at once. This is
45 : precisely the pattern of memory allocation which occurs when parsing:
46 : allocation is performed for each parsed element, and when the the resulting
47 : @ref value is no longer needed, the entire structure is destroyed. However,
48 : it is not suited for modifying the value after parsing is complete;
49 : reallocations waste memory, since the older buffer is not reclaimed until
50 : the resource is destroyed.
51 :
52 : @par Example
53 :
54 : This parses a JSON text into a value which uses a local stack buffer, then
55 : prints the result.
56 :
57 : @code
58 : unsigned char buf[ 4000 ];
59 : monotonic_resource mr( buf );
60 :
61 : // Parse the string, using our memory resource
62 : auto const jv = parse( "[1,2,3]", &mr );
63 :
64 : // Print the JSON
65 : std::cout << jv;
66 : @endcode
67 :
68 : @note The total amount of memory dynamically allocated is monotonically
69 : increasing; That is, it never decreases.
70 :
71 : @par Thread Safety
72 : Members of the same instance may not be
73 : called concurrently.
74 :
75 : @see
76 : https://en.wikipedia.org/wiki/Region-based_memory_management
77 : */
78 : class
79 : BOOST_JSON_DECL
80 : BOOST_SYMBOL_VISIBLE
81 : monotonic_resource final
82 : : public container::pmr::memory_resource
83 : {
84 : struct block;
85 : struct block_base
86 : {
87 : void* p;
88 : std::size_t avail;
89 : std::size_t size;
90 : block_base* next;
91 : };
92 :
93 : block_base buffer_;
94 : block_base* head_ = &buffer_;
95 : std::size_t next_size_ = 1024;
96 : storage_ptr upstream_;
97 :
98 : static constexpr std::size_t min_size_ = 1024;
99 : inline static constexpr std::size_t max_size();
100 : inline static std::size_t round_pow2(
101 : std::size_t n) noexcept;
102 : inline static std::size_t next_pow2(
103 : std::size_t n) noexcept;
104 :
105 : public:
106 : /** Assignment operator.
107 :
108 : Copy assignment operator is deleted. This type is not copyable or
109 : movable.
110 : */
111 : monotonic_resource& operator=(
112 : monotonic_resource const&) = delete;
113 :
114 : /** Destructor.
115 :
116 : Deallocates all the memory owned by this resource.
117 :
118 : @par Effects
119 : @code
120 : release();
121 : @endcode
122 :
123 : @par Complexity
124 : Linear in the number of deallocations performed.
125 :
126 : @par Exception Safety
127 : No-throw guarantee.
128 : */
129 : ~monotonic_resource();
130 :
131 : /** Constructors.
132 :
133 : Construct the resource.
134 :
135 : @li **(1)** indicates that the first internal dynamic allocation shall
136 : be at least `initial_size` bytes.
137 : @li **(2)**--**(5)** indicate that subsequent allocations should use
138 : the specified caller-owned buffer. When this buffer is exhausted,
139 : dynamic allocations from the upstream resource are made.
140 : @li **(6)** copy constructor is deleted. This type is not copyable or
141 : movable.
142 :
143 : None of the constructors performs any dynamic allocations.
144 :
145 : @par Complexity
146 : Constant.
147 :
148 : @par Exception Safety
149 : No-throw guarantee.
150 :
151 : @param initial_size The size of the first internal dynamic allocation.
152 : If this is lower than the implementation-defined lower limit,
153 : then the lower limit is used instead.
154 : @param upstream An optional upstream memory resource to use for
155 : performing internal dynamic allocations. If this parameter is
156 : omitted, the \<\<default_memory_resource,default resource\>\> is
157 : used.
158 :
159 : @{
160 : */
161 : explicit
162 : monotonic_resource(
163 : std::size_t initial_size = 1024,
164 : storage_ptr upstream = {}) noexcept;
165 :
166 : /** Overload
167 :
168 : @param buffer The buffer to use. Ownership is not transferred; the
169 : caller is responsible for ensuring that the lifetime of the
170 : buffer extends until the resource is destroyed.
171 : @param size The number of valid bytes pointed to by `buffer`.
172 : @param upstream
173 : */
174 : monotonic_resource(
175 : unsigned char* buffer,
176 : std::size_t size,
177 : storage_ptr upstream = {}) noexcept;
178 :
179 : #if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
180 : /// Overload
181 : monotonic_resource(
182 : std::byte* buffer,
183 : std::size_t size,
184 : storage_ptr upstream) noexcept
185 : : monotonic_resource(reinterpret_cast<
186 : unsigned char*>(buffer), size,
187 : std::move(upstream))
188 : {
189 : }
190 : #endif
191 :
192 : /// Overload
193 : template<std::size_t N>
194 : explicit
195 2 : monotonic_resource(
196 : unsigned char(&buffer)[N],
197 : storage_ptr upstream = {}) noexcept
198 : : monotonic_resource(&buffer[0],
199 2 : N, std::move(upstream))
200 : {
201 2 : }
202 :
203 : #if defined(__cpp_lib_byte) || defined(BOOST_JSON_DOCS)
204 : /// Overload
205 : template<std::size_t N>
206 : explicit
207 : monotonic_resource(
208 : std::byte(&buffer)[N],
209 : storage_ptr upstream = {}) noexcept
210 : : monotonic_resource(&buffer[0],
211 : N, std::move(upstream))
212 : {
213 : }
214 : #endif
215 :
216 : #ifndef BOOST_JSON_DOCS
217 : // Safety net for accidental buffer overflows
218 : template<std::size_t N>
219 : monotonic_resource(
220 : unsigned char(&buffer)[N],
221 : std::size_t n,
222 : storage_ptr upstream = {}) noexcept
223 : : monotonic_resource(&buffer[0],
224 : n, std::move(upstream))
225 : {
226 : // If this goes off, check your parameters
227 : // closely, chances are you passed an array
228 : // thinking it was a pointer.
229 : BOOST_ASSERT(n <= N);
230 : }
231 :
232 : #ifdef __cpp_lib_byte
233 : // Safety net for accidental buffer overflows
234 : template<std::size_t N>
235 : monotonic_resource(
236 : std::byte(&buffer)[N],
237 : std::size_t n,
238 : storage_ptr upstream = {}) noexcept
239 : : monotonic_resource(&buffer[0],
240 : n, std::move(upstream))
241 : {
242 : // If this goes off, check your parameters
243 : // closely, chances are you passed an array
244 : // thinking it was a pointer.
245 : BOOST_ASSERT(n <= N);
246 : }
247 : #endif
248 : #endif
249 :
250 : /// Overload
251 : monotonic_resource(
252 : monotonic_resource const&) = delete;
253 : /// @}
254 :
255 : /** Release all allocated memory.
256 :
257 : This function deallocates all allocated memory.
258 : If an initial buffer was provided upon construction,
259 : then all of the bytes will be available again for
260 : allocation. Allocated memory is deallocated even
261 : if deallocate has not been called for some of
262 : the allocated blocks.
263 :
264 : @par Complexity
265 : Linear in the number of deallocations performed.
266 :
267 : @par Exception Safety
268 : No-throw guarantee.
269 : */
270 : void
271 : release() noexcept;
272 :
273 : protected:
274 : #ifndef BOOST_JSON_DOCS
275 : void*
276 : do_allocate(
277 : std::size_t n,
278 : std::size_t align) override;
279 :
280 : void
281 : do_deallocate(
282 : void* p,
283 : std::size_t n,
284 : std::size_t align) override;
285 :
286 : bool
287 : do_is_equal(
288 : memory_resource const& mr) const noexcept override;
289 : #endif
290 : };
291 :
292 : #ifdef _MSC_VER
293 : #pragma warning(pop)
294 : #endif
295 :
296 : template<>
297 : struct is_deallocate_trivial<
298 : monotonic_resource>
299 : {
300 : static constexpr bool value = true;
301 : };
302 :
303 : } // namespace json
304 : } // namespace boost
305 :
306 : #endif
|