GCC Code Coverage Report


Directory: libs/json/include/boost/json/
File: impl/value_stack.ipp
Date: 2025-12-23 17:20:53
Exec Total Coverage
Lines: 198 199 99.5%
Functions: 37 37 100.0%
Branches: 57 69 82.6%

Line Branch Exec Source
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/json
8 //
9
10 #ifndef BOOST_JSON_IMPL_VALUE_STACK_IPP
11 #define BOOST_JSON_IMPL_VALUE_STACK_IPP
12
13 #include <boost/json/value_stack.hpp>
14 #include <cstring>
15 #include <stdexcept>
16 #include <utility>
17
18 namespace boost {
19 namespace json {
20
21 //--------------------------------------
22
23 2076892 value_stack::
24 stack::
25 ~stack()
26 {
27 2076892 clear();
28
2/2
✓ Branch 0 taken 75338 times.
✓ Branch 1 taken 2001554 times.
2076892 if( begin_ != temp_ &&
29
2/2
✓ Branch 0 taken 75330 times.
✓ Branch 1 taken 8 times.
75338 begin_ != nullptr)
30 75330 sp_->deallocate(
31 75330 begin_,
32 75330 (end_ - begin_) *
33 sizeof(value));
34 2076892 }
35
36 2076892 value_stack::
37 stack::
38 stack(
39 storage_ptr sp,
40 void* temp,
41 2076892 std::size_t size) noexcept
42 2076892 : sp_(std::move(sp))
43 2076892 , temp_(temp)
44 {
45
2/2
✓ Branch 0 taken 2001516 times.
✓ Branch 1 taken 75376 times.
2076892 if(size >= min_size_ *
46 sizeof(value))
47 {
48 2001516 begin_ = reinterpret_cast<
49 value*>(temp);
50 2001516 top_ = begin_;
51 2001516 end_ = begin_ +
52 2001516 size / sizeof(value);
53 }
54 else
55 {
56 75376 begin_ = nullptr;
57 75376 top_ = nullptr;
58 75376 end_ = nullptr;
59 }
60 2076892 }
61
62 void
63 4153128 value_stack::
64 stack::
65 run_dtors(bool b) noexcept
66 {
67 4153128 run_dtors_ = b;
68 4153128 }
69
70 std::size_t
71 4213790 value_stack::
72 stack::
73 size() const noexcept
74 {
75 4213790 return top_ - begin_;
76 }
77
78 bool
79 64464 value_stack::
80 stack::
81 has_chars()
82 {
83 64464 return chars_ != 0;
84 }
85
86 //--------------------------------------
87
88 // destroy the values but
89 // not the stack allocation.
90 void
91 6230020 value_stack::
92 stack::
93 clear() noexcept
94 {
95
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 6229936 times.
6230020 if(top_ != begin_)
96 {
97
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
84 if(run_dtors_)
98 84 for(auto it = top_;
99
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 84 times.
335 it-- != begin_;)
100 251 it->~value();
101 84 top_ = begin_;
102 }
103 6230020 chars_ = 0;
104 6230020 }
105
106 void
107 1868 value_stack::
108 stack::
109 maybe_grow()
110 {
111
2/2
✓ Branch 0 taken 266 times.
✓ Branch 1 taken 1602 times.
1868 if(top_ >= end_)
112 266 grow_one();
113 1868 }
114
115 // make room for at least one more value
116 void
117 66499 value_stack::
118 stack::
119 grow_one()
120 {
121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66499 times.
66499 BOOST_ASSERT(chars_ == 0);
122 66499 std::size_t const capacity =
123 66499 end_ - begin_;
124 66499 std::size_t new_cap = min_size_;
125 // VFALCO check overflow here
126
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 66499 times.
66541 while(new_cap < capacity + 1)
127 42 new_cap <<= 1;
128 auto const begin =
129 reinterpret_cast<value*>(
130 66499 sp_->allocate(
131 new_cap * sizeof(value)));
132 66499 std::size_t const cur_size = top_ - begin_;
133
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 66488 times.
66499 if(begin_)
134 {
135 11 std::memcpy(
136 reinterpret_cast<char*>(begin),
137 11 reinterpret_cast<char*>(begin_),
138 11 size() * sizeof(value));
139
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 2 times.
11 if(begin_ != temp_)
140 9 sp_->deallocate(begin_,
141 capacity * sizeof(value));
142 }
143 // book-keeping
144 66499 top_ = begin + cur_size;
145 66499 end_ = begin + new_cap;
146 66499 begin_ = begin;
147 66499 }
148
149 // make room for nchars additional characters.
150 void
151 16168 value_stack::
152 stack::
153 grow(std::size_t nchars)
154 {
155 // needed capacity in values
156 std::size_t const needed =
157 16168 size() +
158 16168 1 +
159 16168 ((chars_ + nchars +
160 16168 sizeof(value) - 1) /
161 16168 sizeof(value));
162 16168 std::size_t const capacity =
163 16168 end_ - begin_;
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16168 times.
16168 BOOST_ASSERT(
165 needed > capacity);
166 16168 std::size_t new_cap = min_size_;
167 // VFALCO check overflow here
168
2/2
✓ Branch 0 taken 41647 times.
✓ Branch 1 taken 16168 times.
57815 while(new_cap < needed)
169 41647 new_cap <<= 1;
170 auto const begin =
171 reinterpret_cast<value*>(
172 16168 sp_->allocate(
173 new_cap * sizeof(value)));
174 16168 std::size_t const cur_size = top_ - begin_;
175
2/2
✓ Branch 0 taken 7328 times.
✓ Branch 1 taken 8840 times.
16168 if(begin_)
176 {
177 std::size_t amount =
178 7328 size() * sizeof(value);
179
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7328 times.
7328 if(chars_ > 0)
180 amount += sizeof(value) + chars_;
181 7328 std::memcpy(
182 reinterpret_cast<char*>(begin),
183 7328 reinterpret_cast<char*>(begin_),
184 amount);
185
1/2
✓ Branch 0 taken 7328 times.
✗ Branch 1 not taken.
7328 if(begin_ != temp_)
186 7328 sp_->deallocate(begin_,
187 capacity * sizeof(value));
188 }
189 // book-keeping
190 16168 top_ = begin + cur_size;
191 16168 end_ = begin + new_cap;
192 16168 begin_ = begin;
193 16168 }
194
195 //--------------------------------------
196
197 void
198 17145 value_stack::
199 stack::
200 append(string_view s)
201 {
202 17145 std::size_t const bytes_avail =
203 reinterpret_cast<
204 17145 char const*>(end_) -
205 reinterpret_cast<
206 17145 char const*>(top_);
207 // make sure there is room for
208 // pushing one more value without
209 // clobbering the string.
210 34290 if(sizeof(value) + chars_ +
211
2/2
✓ Branch 1 taken 16168 times.
✓ Branch 2 taken 977 times.
17145 s.size() > bytes_avail)
212 16168 grow(s.size());
213
214 // copy the new piece
215 17145 std::memcpy(
216 reinterpret_cast<char*>(
217 17145 top_ + 1) + chars_,
218 17145 s.data(), s.size());
219 17145 chars_ += s.size();
220
221 // ensure a pushed value cannot
222 // clobber the released string.
223
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17145 times.
17145 BOOST_ASSERT(
224 reinterpret_cast<char*>(
225 top_ + 1) + chars_ <=
226 reinterpret_cast<char*>(
227 end_));
228 17145 }
229
230 string_view
231 17020 value_stack::
232 stack::
233 release_string() noexcept
234 {
235 // ensure a pushed value cannot
236 // clobber the released string.
237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17020 times.
17020 BOOST_ASSERT(
238 reinterpret_cast<char*>(
239 top_ + 1) + chars_ <=
240 reinterpret_cast<char*>(
241 end_));
242 17020 auto const n = chars_;
243 17020 chars_ = 0;
244 return { reinterpret_cast<
245 17020 char const*>(top_ + 1), n };
246 }
247
248 // transfer ownership of the top n
249 // elements of the stack to the caller
250 value*
251 2113641 value_stack::
252 stack::
253 release(std::size_t n) noexcept
254 {
255
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2113641 times.
2113641 BOOST_ASSERT(n <= size());
256
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2113641 times.
2113641 BOOST_ASSERT(chars_ == 0);
257 2113641 top_ -= n;
258 2113641 return top_;
259 }
260
261 template<class... Args>
262 value&
263 4239868 value_stack::
264 stack::
265 push(Args&&... args)
266 {
267
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2119934 times.
4239868 BOOST_ASSERT(chars_ == 0);
268
2/2
✓ Branch 0 taken 66233 times.
✓ Branch 1 taken 2053701 times.
4239868 if(top_ >= end_)
269 132466 grow_one();
270 value& jv = detail::access::
271 4239868 construct_value(top_,
272 std::forward<Args>(args)...);
273 4239740 ++top_;
274 4239740 return jv;
275 }
276
277 template<class Unchecked>
278 void
279 73998 value_stack::
280 stack::
281 exchange(Unchecked&& u)
282 {
283
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36999 times.
73998 BOOST_ASSERT(chars_ == 0);
284 union U
285 {
286 value v;
287 36999 U() {}
288 36999 ~U() {}
289 73998 } jv;
290 // construct value on the stack
291 // to avoid clobbering top_[0],
292 // which belongs to `u`.
293 detail::access::
294
1/1
✓ Branch 1 taken 36922 times.
73998 construct_value(
295 73998 &jv.v, std::move(u));
296 73844 std::memcpy(
297 reinterpret_cast<
298 73844 char*>(top_),
299 &jv.v, sizeof(value));
300 73844 ++top_;
301 73998 }
302
303 //----------------------------------------------------------
304
305 2076892 value_stack::
306 ~value_stack()
307 {
308 // default dtor is here so the
309 // definition goes in the library
310 // instead of the caller's TU.
311 2076892 }
312
313 2076892 value_stack::
314 value_stack(
315 storage_ptr sp,
316 unsigned char* temp_buffer,
317 2076892 std::size_t temp_size) noexcept
318 2076892 : st_(
319 2076892 std::move(sp),
320 temp_buffer,
321 2076892 temp_size)
322 {
323 2076892 }
324
325 void
326 4153128 value_stack::
327 reset(storage_ptr sp) noexcept
328 {
329 4153128 st_.clear();
330
331 4153128 sp_.~storage_ptr();
332 12459376 ::new(&sp_) storage_ptr(
333 4153128 pilfer(sp));
334
335 // `stack` needs this
336 // to clean up correctly
337 8306256 st_.run_dtors(
338 4153128 ! sp_.is_not_shared_and_deallocate_is_trivial());
339 4153128 }
340
341 value
342 2076642 value_stack::
343 release() noexcept
344 {
345 // This means the caller did not
346 // cause a single top level element
347 // to be produced.
348
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2076642 times.
2076642 BOOST_ASSERT(st_.size() == 1);
349
350 // give up shared ownership
351 2076642 sp_ = {};
352
353 2076642 return pilfer(*st_.release(1));
354 }
355
356 //----------------------------------------------------------
357
358 void
359 2120 value_stack::
360 push_array(std::size_t n)
361 {
362 // we already have room if n > 0
363
2/2
✓ Branch 0 taken 819 times.
✓ Branch 1 taken 1301 times.
2120 if(BOOST_JSON_UNLIKELY(n == 0))
364
1/1
✓ Branch 1 taken 819 times.
819 st_.maybe_grow();
365 detail::unchecked_array ua(
366 2120 st_.release(n), n, sp_);
367
1/1
✓ Branch 2 taken 2082 times.
2120 st_.exchange(std::move(ua));
368 2120 }
369
370 void
371 34879 value_stack::
372 push_object(std::size_t n)
373 {
374 // we already have room if n > 0
375
2/2
✓ Branch 0 taken 1049 times.
✓ Branch 1 taken 33830 times.
34879 if(BOOST_JSON_UNLIKELY(n == 0))
376
1/1
✓ Branch 1 taken 1049 times.
1049 st_.maybe_grow();
377 detail::unchecked_object uo(
378 34879 st_.release(n * 2), n, sp_);
379
1/1
✓ Branch 2 taken 34840 times.
34879 st_.exchange(std::move(uo));
380 34879 }
381
382 void
383 17145 value_stack::
384 push_chars(
385 string_view s)
386 {
387 17145 st_.append(s);
388 17145 }
389
390 void
391 38356 value_stack::
392 push_key(
393 string_view s)
394 {
395
2/2
✓ Branch 1 taken 30296 times.
✓ Branch 2 taken 8060 times.
38356 if(! st_.has_chars())
396 {
397
1/1
✓ Branch 1 taken 30236 times.
30296 st_.push(detail::key_t{}, s, sp_);
398 30236 return;
399 }
400 8060 auto part = st_.release_string();
401
1/1
✓ Branch 1 taken 8060 times.
8060 st_.push(detail::key_t{}, part, s, sp_);
402 }
403
404 void
405 26108 value_stack::
406 push_string(
407 string_view s)
408 {
409
2/2
✓ Branch 1 taken 17148 times.
✓ Branch 2 taken 8960 times.
26108 if(! st_.has_chars())
410 {
411 // fast path
412
1/1
✓ Branch 1 taken 17144 times.
17148 st_.push(s, sp_);
413 17144 return;
414 }
415
416 // VFALCO We could add a special
417 // private ctor to string that just
418 // creates uninitialized space,
419 // to reduce member function calls.
420 8960 auto part = st_.release_string();
421 17920 auto& str = st_.push(
422
1/1
✓ Branch 1 taken 8960 times.
8960 string_kind, sp_).get_string();
423
1/1
✓ Branch 1 taken 8960 times.
8960 str.reserve(
424 8960 part.size() + s.size());
425 8960 std::memcpy(
426 8960 str.data(),
427 8960 part.data(), part.size());
428 8960 std::memcpy(
429 8960 str.data() + part.size(),
430 8960 s.data(), s.size());
431 8960 str.grow(part.size() + s.size());
432 }
433
434 void
435 5815 value_stack::
436 push_int64(
437 int64_t i)
438 {
439 5815 st_.push(i, sp_);
440 5815 }
441
442 void
443 70 value_stack::
444 push_uint64(
445 uint64_t u)
446 {
447 70 st_.push(u, sp_);
448 70 }
449
450 void
451 2039817 value_stack::
452 push_double(
453 double d)
454 {
455 2039817 st_.push(d, sp_);
456 2039817 }
457
458 void
459 400 value_stack::
460 push_bool(
461 bool b)
462 {
463 400 st_.push(b, sp_);
464 400 }
465
466 void
467 9368 value_stack::
468 push_null()
469 {
470
1/1
✓ Branch 1 taken 9368 times.
9368 st_.push(nullptr, sp_);
471 9368 }
472
473 } // namespace json
474 } // namespace boost
475
476 #endif
477