Coverage Report

Created: 2025-07-18 06:46

/src/jsoncons/include/jsoncons_ext/jsonpointer/jsonpointer.hpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2013-2025 Daniel Parker
2
// Distributed under the Boost license, Version 1.0.
3
// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
4
5
// See https://github.com/danielaparker/jsoncons for latest version
6
7
#ifndef JSONCONS_EXT_JSONPOINTER_JSONPOINTER_HPP
8
#define JSONCONS_EXT_JSONPOINTER_JSONPOINTER_HPP
9
10
#include <cstddef>
11
#include <iostream>
12
#include <memory>
13
#include <string>
14
#include <system_error> // system_error
15
#include <type_traits> // std::enable_if, std::true_type
16
#include <utility> // std::move
17
#include <vector>
18
19
#include <jsoncons/utility/write_number.hpp>
20
#include <jsoncons/json_type.hpp>
21
#include <jsoncons/utility/more_type_traits.hpp>
22
23
#include <jsoncons_ext/jsonpointer/jsonpointer_error.hpp>
24
25
namespace jsoncons { 
26
namespace jsonpointer {
27
28
    namespace detail {
29
30
    enum class pointer_state 
31
    {
32
        start,
33
        escaped,
34
        new_token,
35
        part
36
    };
37
38
    } // namespace detail
39
40
    template <typename CharT,typename Allocator=std::allocator<CharT>>
41
    std::basic_string<CharT,std::char_traits<CharT>,Allocator> escape(jsoncons::basic_string_view<CharT> s, const Allocator& = Allocator())
42
0
    {
43
0
        std::basic_string<CharT,std::char_traits<CharT>,Allocator> result;
44
45
0
        for (auto c : s)
46
0
        {
47
0
            if (JSONCONS_UNLIKELY(c == '~'))
48
0
            {
49
0
                result.push_back('~');
50
0
                result.push_back('0');
51
0
            }
52
0
            else if (JSONCONS_UNLIKELY(c == '/'))
53
0
            {
54
0
                result.push_back('~');
55
0
                result.push_back('1');
56
0
            }
57
0
            else
58
0
            {
59
0
                result.push_back(c);
60
0
            }
61
0
        }
62
0
        return result;
63
0
    }
64
65
    template <typename CharT>
66
    std::basic_string<CharT> escape_string(const std::basic_string<CharT>& s)
67
    {
68
        std::basic_string<CharT> result;
69
        for (auto c : s)
70
        {
71
            switch (c)
72
            {
73
                case '~':
74
                    result.push_back('~');
75
                    result.push_back('0');
76
                    break;
77
                case '/':
78
                    result.push_back('~');
79
                    result.push_back('1');
80
                    break;
81
                default:
82
                    result.push_back(c);
83
                    break;
84
            }
85
        }
86
        return result;
87
    }
88
89
    // basic_json_pointer
90
91
    template <typename CharT>
92
    class basic_json_pointer
93
    {
94
    public:
95
        // Member types
96
        using char_type = CharT;
97
        using string_type = std::basic_string<char_type>;
98
        using string_view_type = jsoncons::basic_string_view<char_type>;
99
        using const_iterator = typename std::vector<string_type>::const_iterator;
100
        using iterator = const_iterator;
101
        using const_reverse_iterator = typename std::vector<string_type>::const_reverse_iterator;
102
        using reverse_iterator = const_reverse_iterator;
103
    private:
104
        std::vector<string_type> tokens_;
105
    public:
106
        // Constructors
107
        basic_json_pointer()
108
        {
109
        }
110
111
        basic_json_pointer(const std::vector<string_type>& tokens)
112
            : tokens_(tokens)
113
        {
114
        }
115
116
        basic_json_pointer(std::vector<string_type>&& tokens)
117
            : tokens_(std::move(tokens))
118
        {
119
        }
120
121
        explicit basic_json_pointer(const string_view_type& s)
122
        {
123
            std::error_code ec;
124
            auto jp = parse(s, ec);
125
            if (JSONCONS_UNLIKELY(ec))
126
            {
127
                JSONCONS_THROW(jsonpointer_error(ec));
128
            }
129
            tokens_ = std::move(jp.tokens_);
130
        }
131
132
        explicit basic_json_pointer(const string_view_type& s, std::error_code& ec)
133
        {
134
            auto jp = parse(s, ec);
135
            if (!ec)
136
            {
137
                tokens_ = std::move(jp.tokens_);
138
            }
139
        }
140
141
        basic_json_pointer(const basic_json_pointer&) = default;
142
143
        basic_json_pointer(basic_json_pointer&&) = default;
144
145
        static basic_json_pointer parse(const string_view_type& input, std::error_code& ec)
146
        {
147
            std::vector<string_type> tokens;
148
            if (input.empty())
149
            {
150
                return basic_json_pointer<CharT>();
151
            }
152
153
            const char_type* p = input.data();
154
            const char_type* pend = input.data() + input.size();
155
            string_type unescaped;
156
157
            auto state = jsonpointer::detail::pointer_state::start;
158
            string_type buffer;
159
160
            while (p < pend)
161
            {
162
                    switch (state)
163
                    {
164
                        case jsonpointer::detail::pointer_state::start: 
165
                            switch (*p)
166
                            {
167
                                case '/':
168
                                    state = jsonpointer::detail::pointer_state::new_token;
169
                                    break;
170
                                default:
171
                                    ec = jsonpointer_errc::expected_slash;
172
                                    return basic_json_pointer();
173
                            };
174
                            break;
175
                        case jsonpointer::detail::pointer_state::part:
176
                            state = jsonpointer::detail::pointer_state::new_token; 
177
                            JSONCONS_FALLTHROUGH;
178
179
                        case jsonpointer::detail::pointer_state::new_token: 
180
                            switch (*p)
181
                            {
182
                                case '/':
183
                                    tokens.push_back(buffer);
184
                                    buffer.clear();
185
                                    state = jsonpointer::detail::pointer_state::part; 
186
                                    break;
187
                                case '~':
188
                                    state = jsonpointer::detail::pointer_state::escaped;
189
                                    break;
190
                                default:
191
                                    buffer.push_back(*p);
192
                                    break;
193
                            };
194
                            break;
195
                        case jsonpointer::detail::pointer_state::escaped: 
196
                            switch (*p)
197
                            {
198
                                case '0':
199
                                    buffer.push_back('~');
200
                                    state = jsonpointer::detail::pointer_state::new_token;
201
                                    break;
202
                                case '1':
203
                                    buffer.push_back('/');
204
                                    state = jsonpointer::detail::pointer_state::new_token;
205
                                    break;
206
                                default:
207
                                    ec = jsonpointer_errc::expected_0_or_1;
208
                                    return basic_json_pointer();
209
                            };
210
                            break;
211
                    }
212
                    ++p;
213
            }
214
            if (state == jsonpointer::detail::pointer_state::escaped)
215
            {
216
                ec = jsonpointer_errc::expected_0_or_1;
217
                return basic_json_pointer();
218
            }
219
            if (state == jsonpointer::detail::pointer_state::new_token || state == jsonpointer::detail::pointer_state::part)
220
            {
221
                tokens.push_back(buffer);
222
            }
223
            return basic_json_pointer(tokens);
224
        }
225
226
        // operator=
227
        basic_json_pointer& operator=(const basic_json_pointer&) = default;
228
229
        basic_json_pointer& operator=(basic_json_pointer&&) = default;
230
231
        // Modifiers
232
233
        void clear()
234
        {
235
            tokens_.clear();
236
        }
237
238
        basic_json_pointer& append(const string_type& s) 
239
        {
240
            tokens_.push_back(s);
241
            return *this;
242
        }
243
244
        template <typename IntegerType>
245
        typename std::enable_if<ext_traits::is_integer<IntegerType>::value, basic_json_pointer&>::type
246
        append(IntegerType val)
247
        {
248
            string_type s;
249
            jsoncons::utility::from_integer(val, s);
250
            tokens_.push_back(s);
251
252
            return *this;
253
        }
254
255
        basic_json_pointer& operator/=(const string_type& s) 
256
        {
257
            tokens_.push_back(s);
258
            return *this;
259
        }
260
261
        template <typename IntegerType>
262
        typename std::enable_if<ext_traits::is_integer<IntegerType>::value, basic_json_pointer&>::type
263
        operator/=(IntegerType val)
264
        {
265
            string_type s;
266
            jsoncons::utility::from_integer(val, s);
267
            tokens_.push_back(s);
268
269
            return *this;
270
        }
271
272
        basic_json_pointer& operator+=(const basic_json_pointer& p)
273
        {
274
            for (const auto& s : p.tokens_)
275
            {
276
                tokens_.push_back(s);
277
            }
278
            return *this;
279
        }
280
281
        // Accessors
282
        bool empty() const
283
        {
284
          return tokens_.empty();
285
        }
286
287
        string_type string() const
288
        {
289
            return to_string();
290
        }
291
292
        string_type to_string() const
293
0
        {
294
0
            string_type buffer;
295
0
            for (const auto& token : tokens_)
296
0
            {
297
0
                buffer.push_back('/');
298
0
                for (auto c : token)
299
0
                {
300
0
                    switch (c)
301
0
                    {
302
0
                        case '~':
303
0
                            buffer.push_back('~');
304
0
                            buffer.push_back('0');
305
0
                            break;
306
0
                        case '/':
307
0
                            buffer.push_back('~');
308
0
                            buffer.push_back('1');
309
0
                            break;
310
0
                        default:
311
0
                            buffer.push_back(c);
312
0
                            break;
313
0
                    }
314
0
                }
315
0
            }
316
0
            return buffer;
317
0
        }
Unexecuted instantiation: jsoncons::jsonpointer::basic_json_pointer<char>::to_string() const
Unexecuted instantiation: jsoncons::jsonpointer::basic_json_pointer<wchar_t>::to_string() const
318
319
        // Iterators
320
        iterator begin() const
321
        {
322
            return tokens_.begin();
323
        }
324
        iterator end() const
325
        {
326
            return tokens_.end();
327
        }
328
329
        reverse_iterator rbegin() const
330
        {
331
            return tokens_.rbegin();
332
        }
333
        reverse_iterator rend() const
334
        {
335
            return tokens_.rend();
336
        }
337
338
        // Non-member functions
339
        friend basic_json_pointer<CharT> operator/(const basic_json_pointer<CharT>& lhs, const string_type& rhs)
340
        {
341
            basic_json_pointer<CharT> p(lhs);
342
            p /= rhs;
343
            return p;
344
        }
345
346
        friend basic_json_pointer<CharT> operator+( const basic_json_pointer<CharT>& lhs, const basic_json_pointer<CharT>& rhs )
347
        {
348
            basic_json_pointer<CharT> p(lhs);
349
            p += rhs;
350
            return p;
351
        }
352
353
        friend bool operator==( const basic_json_pointer& lhs, const basic_json_pointer& rhs )
354
        {
355
            return lhs.tokens_ == rhs.tokens_;
356
        }
357
358
        friend bool operator!=( const basic_json_pointer& lhs, const basic_json_pointer& rhs )
359
        {
360
            return lhs.tokens_ != rhs.tokens_;
361
        }
362
363
        friend std::basic_ostream<CharT>&
364
        operator<<( std::basic_ostream<CharT>& os, const basic_json_pointer<CharT>& p )
365
        {
366
            os << p.to_string();
367
            return os;
368
        }
369
    };
370
371
    template <typename CharT,typename IntegerType>
372
    typename std::enable_if<ext_traits::is_integer<IntegerType>::value, basic_json_pointer<CharT>>::type
373
    operator/(const basic_json_pointer<CharT>& lhs, IntegerType rhs)
374
    {
375
        basic_json_pointer<CharT> p(lhs);
376
        p /= rhs;
377
        return p;
378
    }
379
380
    using json_pointer = basic_json_pointer<char>;
381
    using wjson_pointer = basic_json_pointer<wchar_t>;
382
383
    inline
384
    std::string to_string(const json_pointer& ptr)
385
0
    {
386
0
        return ptr.to_string();
387
0
    }
388
389
    inline
390
    std::wstring to_wstring(const wjson_pointer& ptr)
391
0
    {
392
0
        return ptr.to_string();
393
0
    }
394
395
    namespace detail {
396
397
    template <typename Json>
398
    const Json* resolve(const Json* current, const typename Json::string_view_type& buffer, std::error_code& ec)
399
    {
400
        if (current->is_array())
401
        {
402
            if (buffer.size() == 1 && buffer[0] == '-')
403
            {
404
                ec = jsonpointer_errc::index_exceeds_array_size;
405
                return current;
406
            }
407
            std::size_t index{0};
408
            auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index);
409
            if (!result)
410
            {
411
                ec = jsonpointer_errc::invalid_index;
412
                return current;
413
            }
414
            if (index >= current->size())
415
            {
416
                ec = jsonpointer_errc::index_exceeds_array_size;
417
                return current;
418
            }
419
            current = std::addressof(current->at(index));
420
        }
421
        else if (current->is_object())
422
        {
423
            if (!current->contains(buffer))
424
            {
425
                ec = jsonpointer_errc::key_not_found;
426
                return current;
427
            }
428
            current = std::addressof(current->at(buffer));
429
        }
430
        else
431
        {
432
            ec = jsonpointer_errc::expected_object_or_array;
433
            return current;
434
        }
435
        return current;
436
    }
437
438
    template <typename Json>
439
    Json* resolve(Json* current, const typename Json::string_view_type& buffer, bool create_if_missing, std::error_code& ec)
440
    {
441
        if (current->is_array())
442
        {
443
            if (buffer.size() == 1 && buffer[0] == '-')
444
            {
445
                ec = jsonpointer_errc::index_exceeds_array_size;
446
                return current;
447
            }
448
            std::size_t index{0};
449
            auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index);
450
            if (!result)
451
            {
452
                ec = jsonpointer_errc::invalid_index;
453
                return current;
454
            }
455
            if (index >= current->size())
456
            {
457
                ec = jsonpointer_errc::index_exceeds_array_size;
458
                return current;
459
            }
460
            current = std::addressof(current->at(index));
461
        }
462
        else if (current->is_object())
463
        {
464
            if (!current->contains(buffer))
465
            {
466
                if (create_if_missing)
467
                {
468
                    auto r = current->try_emplace(buffer, Json());
469
                    current = std::addressof(r.first->value());
470
                }
471
                else
472
                {
473
                    ec = jsonpointer_errc::key_not_found;
474
                    return current;
475
                }
476
            }
477
            else
478
            {
479
                current = std::addressof(current->at(buffer));
480
            }
481
        }
482
        else
483
        {
484
            ec = jsonpointer_errc::expected_object_or_array;
485
            return current;
486
        }
487
        return current;
488
    }
489
490
    } // namespace detail
491
492
    // get
493
494
    template <typename Json>
495
    Json& get(Json& root, 
496
              const basic_json_pointer<typename Json::char_type>& location, 
497
              bool create_if_missing,
498
              std::error_code& ec)
499
    {
500
        if (location.empty())
501
        {
502
            return root;
503
        }
504
505
        Json* current = std::addressof(root);
506
        auto it = location.begin();
507
        auto end = location.end();
508
        while (it != end)
509
        {
510
            current = jsoncons::jsonpointer::detail::resolve(current, *it, create_if_missing, ec);
511
            if (JSONCONS_UNLIKELY(ec))
512
                return *current;
513
            ++it;
514
        }
515
        return *current;
516
    }
517
518
    template <typename Json,typename StringSource>
519
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,Json&>::type
520
    get(Json& root, 
521
        const StringSource& location_str, 
522
        bool create_if_missing,
523
        std::error_code& ec)
524
    {
525
        auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
526
        if (JSONCONS_UNLIKELY(ec))
527
        {
528
            return root;
529
        }
530
        return get(root, jsonptr, create_if_missing, ec);
531
    }
532
533
    template <typename Json>
534
    const Json& get(const Json& root, 
535
                    const basic_json_pointer<typename Json::char_type>& location, 
536
                    std::error_code& ec)
537
    {
538
        if (location.empty())
539
        {
540
            return root;
541
        }
542
543
        const Json* current = std::addressof(root);
544
        auto it = location.begin();
545
        auto end = location.end();
546
        while (it != end)
547
        {
548
            current = jsoncons::jsonpointer::detail::resolve(current, *it, ec);
549
            if (JSONCONS_UNLIKELY(ec))
550
                return *current;
551
            ++it;
552
        }
553
        return *current;
554
    }
555
556
    template <typename Json,typename StringSource>
557
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,const Json&>::type
558
    get(const Json& root, 
559
        const StringSource& location_str, 
560
        std::error_code& ec)
561
    {
562
        auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
563
        if (JSONCONS_UNLIKELY(ec))
564
        {
565
            return root;
566
        }
567
        return get(root, jsonptr, ec);
568
    }
569
570
    template <typename Json>
571
    Json& get(Json& root, 
572
              const basic_json_pointer<typename Json::char_type>& location, 
573
              std::error_code& ec)
574
    {
575
        return get(root, location, false, ec);
576
    }
577
578
    template <typename Json,typename StringSource>
579
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,Json&>::type
580
    get(Json& root, 
581
        const StringSource& location_str, 
582
        std::error_code& ec)
583
    {
584
        return get(root, location_str, false, ec);
585
    }
586
587
    template <typename Json>
588
    Json& get(Json& root, 
589
              const basic_json_pointer<typename Json::char_type>& location,
590
              bool create_if_missing = false)
591
    {
592
        std::error_code ec;
593
        Json& j = get(root, location, create_if_missing, ec);
594
        if (JSONCONS_UNLIKELY(ec))
595
        {
596
            JSONCONS_THROW(jsonpointer_error(ec));
597
        }
598
        return j;
599
    }
600
601
    template <typename Json,typename StringSource>
602
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,Json&>::type
603
    get(Json& root, 
604
              const StringSource& location_str,
605
              bool create_if_missing = false)
606
    {
607
        std::error_code ec;
608
        Json& result = get(root, location_str, create_if_missing, ec);
609
        if (JSONCONS_UNLIKELY(ec))
610
        {
611
            JSONCONS_THROW(jsonpointer_error(ec));
612
        }
613
        return result;
614
    }
615
616
    template <typename Json>
617
    const Json& get(const Json& root, const basic_json_pointer<typename Json::char_type>& location)
618
    {
619
        std::error_code ec;
620
        const Json& j = get(root, location, ec);
621
        if (JSONCONS_UNLIKELY(ec))
622
        {
623
            JSONCONS_THROW(jsonpointer_error(ec));
624
        }
625
        return j;
626
    }
627
628
    template <typename Json,typename StringSource>
629
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,const Json&>::type
630
    get(const Json& root, const StringSource& location_str)
631
    {
632
        std::error_code ec;
633
        const Json& j = get(root, location_str, ec);
634
        if (JSONCONS_UNLIKELY(ec))
635
        {
636
            JSONCONS_THROW(jsonpointer_error(ec));
637
        }
638
        return j;
639
    }
640
641
    // contains
642
643
    template <typename Json>
644
    bool contains(const Json& root, const basic_json_pointer<typename Json::char_type>& location)
645
    {
646
        std::error_code ec;
647
        get(root, location, ec);
648
        return !ec ? true : false;
649
    }
650
651
    template <typename Json,typename StringSource>
652
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,bool>::type
653
    contains(const Json& root, const StringSource& location_str)
654
    {
655
        std::error_code ec;
656
        get(root, location_str, ec);
657
        return !ec ? true : false;
658
    }
659
660
    template <typename Json,typename T>
661
    void add(Json& root, 
662
             const basic_json_pointer<typename Json::char_type>& location, 
663
             T&& value, 
664
             bool create_if_missing,
665
             std::error_code& ec)
666
    {
667
        Json* current = std::addressof(root);
668
669
        std::basic_string<typename Json::char_type> buffer;
670
        auto it = location.begin();
671
        auto end = location.end();
672
        while (it != end)
673
        {
674
            buffer = *it;
675
            ++it;
676
            if (it != end)
677
            {
678
                current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec);
679
                if (JSONCONS_UNLIKELY(ec))
680
                    return;
681
            }
682
        }
683
        if (current->is_array())
684
        {
685
            if (buffer.size() == 1 && buffer[0] == '-')
686
            {
687
                current->emplace_back(std::forward<T>(value));
688
                current = std::addressof(current->at(current->size()-1));
689
            }
690
            else
691
            {
692
                std::size_t index{0};
693
                auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index);
694
                if (!result)
695
                {
696
                    ec = jsonpointer_errc::invalid_index;
697
                    return;
698
                }
699
                if (index > current->size())
700
                {
701
                    ec = jsonpointer_errc::index_exceeds_array_size;
702
                    return;
703
                }
704
                if (index == current->size())
705
                {
706
                    current->emplace_back(std::forward<T>(value));
707
                    current = std::addressof(current->at(current->size()-1));
708
                }
709
                else
710
                {
711
                    auto it2 = current->insert(current->array_range().begin()+index,std::forward<T>(value));
712
                    current = std::addressof(*it2);
713
                }
714
            }
715
        }
716
        else if (current->is_object())
717
        {
718
            auto r = current->insert_or_assign(buffer,std::forward<T>(value));
719
            current = std::addressof(r.first->value());
720
        }
721
        else
722
        {
723
            ec = jsonpointer_errc::expected_object_or_array;
724
            return;
725
        }
726
    }
727
728
    // add
729
    template <typename Json,typename StringSource,typename T>
730
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
731
    add(Json& root, 
732
             const StringSource& location_str, 
733
             T&& value, 
734
             bool create_if_missing,
735
             std::error_code& ec)
736
    {
737
        auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
738
        if (JSONCONS_UNLIKELY(ec))
739
        {
740
            return;
741
        }
742
        add(root, jsonptr, std::forward<T>(value), create_if_missing, ec);
743
    }
744
745
    template <typename Json,typename T>
746
    void add(Json& root, 
747
             const basic_json_pointer<typename Json::char_type>& location, 
748
             T&& value, 
749
             std::error_code& ec)
750
    {
751
        add(root, location, std::forward<T>(value), false, ec);
752
    }
753
754
    template <typename Json,typename StringSource,typename T>
755
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
756
    add(Json& root, 
757
             const StringSource& location_str, 
758
             T&& value, 
759
             std::error_code& ec)
760
    {
761
        add(root, location_str, std::forward<T>(value), false, ec);
762
    }
763
764
    template <typename Json,typename T>
765
    void add(Json& root, 
766
             const basic_json_pointer<typename Json::char_type>& location, 
767
             T&& value,
768
             bool create_if_missing = false)
769
    {
770
        std::error_code ec;
771
        add(root, location, std::forward<T>(value), create_if_missing, ec);
772
        if (JSONCONS_UNLIKELY(ec))
773
        {
774
            JSONCONS_THROW(jsonpointer_error(ec));
775
        }
776
    }
777
778
    template <typename Json,typename StringSource,typename T>
779
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
780
    add(Json& root, 
781
             const StringSource& location_str, 
782
             T&& value,
783
             bool create_if_missing = false)
784
    {
785
        std::error_code ec;
786
        add(root, location_str, std::forward<T>(value), create_if_missing, ec);
787
        if (JSONCONS_UNLIKELY(ec))
788
        {
789
            JSONCONS_THROW(jsonpointer_error(ec));
790
        }
791
    }
792
793
    // add_if_absent
794
795
    template <typename Json,typename T>
796
    void add_if_absent(Json& root, 
797
                       const basic_json_pointer<typename Json::char_type>& location, 
798
                       T&& value, 
799
                       bool create_if_missing,
800
                       std::error_code& ec)
801
    {
802
        Json* current = std::addressof(root);
803
804
        std::basic_string<typename Json::char_type> buffer;
805
        auto it = location.begin();
806
        auto end = location.end();
807
808
        while (it != end)
809
        {
810
            buffer = *it;
811
            ++it;
812
            if (it != end)
813
            {
814
                current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec);
815
                if (JSONCONS_UNLIKELY(ec))
816
                    return;
817
            }
818
        }
819
        if (current->is_array())
820
        {
821
            if (buffer.size() == 1 && buffer[0] == '-')
822
            {
823
                current->emplace_back(std::forward<T>(value));
824
                current = std::addressof(current->at(current->size()-1));
825
            }
826
            else
827
            {
828
                std::size_t index{0};
829
                auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index);
830
                if (!result)
831
                {
832
                    ec = jsonpointer_errc::invalid_index;
833
                    return;
834
                }
835
                if (index > current->size())
836
                {
837
                    ec = jsonpointer_errc::index_exceeds_array_size;
838
                    return;
839
                }
840
                if (index == current->size())
841
                {
842
                    current->emplace_back(std::forward<T>(value));
843
                    current = std::addressof(current->at(current->size()-1));
844
                }
845
                else
846
                {
847
                    auto it2 = current->insert(current->array_range().begin()+index,std::forward<T>(value));
848
                    current = std::addressof(*it2);
849
                }
850
            }
851
        }
852
        else if (current->is_object())
853
        {
854
            if (current->contains(buffer))
855
            {
856
                ec = jsonpointer_errc::key_already_exists;
857
                return;
858
            }
859
            else
860
            {
861
                auto r = current->try_emplace(buffer,std::forward<T>(value));
862
                current = std::addressof(r.first->value());
863
            }
864
        }
865
        else
866
        {
867
            ec = jsonpointer_errc::expected_object_or_array;
868
            return;
869
        }
870
    }
871
872
    template <typename Json,typename StringSource,typename T>
873
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
874
    add_if_absent(Json& root, 
875
                       const StringSource& location_str, 
876
                       T&& value, 
877
                       bool create_if_missing,
878
                       std::error_code& ec)
879
    {
880
        auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
881
        if (JSONCONS_UNLIKELY(ec))
882
        {
883
            return;
884
        }
885
        add_if_absent(root, jsonptr, std::forward<T>(value), create_if_missing, ec);
886
    }
887
888
    template <typename Json,typename StringSource,typename T>
889
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
890
    add_if_absent(Json& root, 
891
                const StringSource& location, 
892
                T&& value, 
893
                std::error_code& ec)
894
    {
895
        add_if_absent(root, location, std::forward<T>(value), false, ec);
896
    }
897
898
    template <typename Json,typename StringSource,typename T>
899
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
900
    add_if_absent(Json& root, 
901
                const StringSource& location_str, 
902
                T&& value,
903
                bool create_if_missing = false)
904
    {
905
        std::error_code ec;
906
        add_if_absent(root, location_str, std::forward<T>(value), create_if_missing, ec);
907
        if (JSONCONS_UNLIKELY(ec))
908
        {
909
            JSONCONS_THROW(jsonpointer_error(ec));
910
        }
911
    }
912
913
    template <typename Json,typename T>
914
    void add_if_absent(Json& root, 
915
                       const basic_json_pointer<typename Json::char_type>& location, 
916
                       T&& value, 
917
                       std::error_code& ec)
918
    {
919
        add_if_absent(root, location, std::forward<T>(value), false, ec);
920
    }
921
922
    template <typename Json,typename T>
923
    void add_if_absent(Json& root, 
924
                const basic_json_pointer<typename Json::char_type>& location, 
925
                T&& value,
926
                bool create_if_missing = false)
927
    {
928
        std::error_code ec;
929
        add_if_absent(root, location, std::forward<T>(value), create_if_missing, ec);
930
        if (JSONCONS_UNLIKELY(ec))
931
        {
932
            JSONCONS_THROW(jsonpointer_error(ec));
933
        }
934
    }
935
936
    // remove
937
938
    template <typename Json>
939
    void remove(Json& root, const basic_json_pointer<typename Json::char_type>& location, std::error_code& ec)
940
    {
941
        Json* current = std::addressof(root);
942
943
        std::basic_string<typename Json::char_type> buffer;
944
        auto it = location.begin();
945
        auto end = location.end();
946
947
        while (it != end)
948
        {
949
            buffer = *it;
950
            ++it;
951
            if (it != end)
952
            {
953
                current = jsoncons::jsonpointer::detail::resolve(current, buffer, false, ec);
954
                if (JSONCONS_UNLIKELY(ec))
955
                    return;
956
            }
957
        }
958
        if (current->is_array())
959
        {
960
            if (buffer.size() == 1 && buffer[0] == '-')
961
            {
962
                ec = jsonpointer_errc::index_exceeds_array_size;
963
                return;
964
            }
965
            else
966
            {
967
                std::size_t index{0};
968
                auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index);
969
                if (!result)
970
                {
971
                    ec = jsonpointer_errc::invalid_index;
972
                    return;
973
                }
974
                if (index >= current->size())
975
                {
976
                    ec = jsonpointer_errc::index_exceeds_array_size;
977
                    return;
978
                }
979
                current->erase(current->array_range().begin()+index);
980
            }
981
        }
982
        else if (current->is_object())
983
        {
984
            if (!current->contains(buffer))
985
            {
986
                ec = jsonpointer_errc::key_not_found;
987
                return;
988
            }
989
            else
990
            {
991
                current->erase(buffer);
992
            }
993
        }
994
        else
995
        {
996
            ec = jsonpointer_errc::expected_object_or_array;
997
            return;
998
        }
999
    }
1000
1001
    template <typename Json,typename StringSource>
1002
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
1003
    remove(Json& root, const StringSource& location_str, std::error_code& ec)
1004
    {
1005
        auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
1006
        if (JSONCONS_UNLIKELY(ec))
1007
        {
1008
            return;
1009
        }
1010
        remove(root, jsonptr, ec);
1011
    }
1012
1013
    template <typename Json,typename StringSource>
1014
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
1015
    remove(Json& root, const StringSource& location_str)
1016
    {
1017
        std::error_code ec;
1018
        remove(root, location_str, ec);
1019
        if (JSONCONS_UNLIKELY(ec))
1020
        {
1021
            JSONCONS_THROW(jsonpointer_error(ec));
1022
        }
1023
    }
1024
1025
    template <typename Json>
1026
    void remove(Json& root, const basic_json_pointer<typename Json::char_type>& location)
1027
    {
1028
        std::error_code ec;
1029
        remove(root, location, ec);
1030
        if (JSONCONS_UNLIKELY(ec))
1031
        {
1032
            JSONCONS_THROW(jsonpointer_error(ec));
1033
        }
1034
    }
1035
1036
    // replace
1037
1038
    template <typename Json,typename T>
1039
    void replace(Json& root, 
1040
                 const basic_json_pointer<typename Json::char_type>& location, 
1041
                 T&& value, 
1042
                 bool create_if_missing,
1043
                 std::error_code& ec)
1044
    {
1045
        Json* current = std::addressof(root);
1046
1047
        std::basic_string<typename Json::char_type> buffer;
1048
        auto it = location.begin();
1049
        auto end = location.end();
1050
1051
        while (it != end)
1052
        {
1053
            buffer = *it;
1054
            ++it;
1055
            if (it != end)
1056
            {
1057
                current = jsoncons::jsonpointer::detail::resolve(current, buffer, create_if_missing, ec);
1058
                if (JSONCONS_UNLIKELY(ec))
1059
                    return;
1060
            }
1061
        }
1062
        if (current->is_array())
1063
        {
1064
            if (buffer.size() == 1 && buffer[0] == '-')
1065
            {
1066
                ec = jsonpointer_errc::index_exceeds_array_size;
1067
                return;
1068
            }
1069
            else
1070
            {
1071
                std::size_t index{};
1072
                auto result = jsoncons::utility::dec_to_integer(buffer.data(), buffer.length(), index);
1073
                if (!result)
1074
                {
1075
                    ec = jsonpointer_errc::invalid_index;
1076
                    return;
1077
                }
1078
                if (index >= current->size())
1079
                {
1080
                    ec = jsonpointer_errc::index_exceeds_array_size;
1081
                    return;
1082
                }
1083
                current->at(index) = std::forward<T>(value);
1084
            }
1085
        }
1086
        else if (current->is_object())
1087
        {
1088
            if (!current->contains(buffer))
1089
            {
1090
                if (create_if_missing)
1091
                {
1092
                    current->try_emplace(buffer,std::forward<T>(value));
1093
                }
1094
                else
1095
                {
1096
                    ec = jsonpointer_errc::key_not_found;
1097
                    return;
1098
                }
1099
            }
1100
            else
1101
            {
1102
                auto r = current->insert_or_assign(buffer,std::forward<T>(value));
1103
                current = std::addressof(r.first->value());
1104
            }
1105
        }
1106
        else
1107
        {
1108
            ec = jsonpointer_errc::expected_object_or_array;
1109
            return;
1110
        }
1111
    }
1112
1113
    template <typename Json,typename StringSource,typename T>
1114
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
1115
    replace(Json& root, 
1116
                 const StringSource& location_str, 
1117
                 T&& value, 
1118
                 bool create_if_missing,
1119
                 std::error_code& ec)
1120
    {
1121
        auto jsonptr = basic_json_pointer<typename Json::char_type>::parse(location_str, ec);
1122
        if (JSONCONS_UNLIKELY(ec))
1123
        {
1124
            return;
1125
        }
1126
        replace(root, jsonptr, std::forward<T>(value), create_if_missing, ec);
1127
    }
1128
1129
    template <typename Json,typename StringSource,typename T>
1130
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
1131
    replace(Json& root, 
1132
                 const StringSource& location_str, 
1133
                 T&& value, 
1134
                 std::error_code& ec)
1135
    {
1136
        replace(root, location_str, std::forward<T>(value), false, ec);
1137
    }
1138
1139
    template <typename Json,typename StringSource,typename T>
1140
    typename std::enable_if<std::is_convertible<StringSource,jsoncons::basic_string_view<typename Json::char_type>>::value,void>::type
1141
    replace(Json& root, 
1142
                 const StringSource& location_str, 
1143
                 T&& value, 
1144
                 bool create_if_missing = false)
1145
    {
1146
        std::error_code ec;
1147
        replace(root, location_str, std::forward<T>(value), create_if_missing, ec);
1148
        if (JSONCONS_UNLIKELY(ec))
1149
        {
1150
            JSONCONS_THROW(jsonpointer_error(ec));
1151
        }
1152
    }
1153
1154
    template <typename Json,typename T>
1155
    void replace(Json& root, 
1156
                 const basic_json_pointer<typename Json::char_type>& location, 
1157
                 T&& value, 
1158
                 std::error_code& ec)
1159
    {
1160
        replace(root, location, std::forward<T>(value), false, ec);
1161
    }
1162
1163
    template <typename Json,typename T>
1164
    void replace(Json& root, 
1165
                 const basic_json_pointer<typename Json::char_type>& location, 
1166
                 T&& value, 
1167
                 bool create_if_missing = false)
1168
    {
1169
        std::error_code ec;
1170
        replace(root, location, std::forward<T>(value), create_if_missing, ec);
1171
        if (JSONCONS_UNLIKELY(ec))
1172
        {
1173
            JSONCONS_THROW(jsonpointer_error(ec));
1174
        }
1175
    }
1176
1177
    template <typename String,typename Result>
1178
    typename std::enable_if<std::is_convertible<typename String::value_type,typename Result::value_type>::value>::type
1179
    escape(const String& s, Result& result)
1180
    {
1181
        for (auto c : s)
1182
        {
1183
            if (c == '~')
1184
            {
1185
                result.push_back('~');
1186
                result.push_back('0');
1187
            }
1188
            else if (c == '/')
1189
            {
1190
                result.push_back('~');
1191
                result.push_back('1');
1192
            }
1193
            else
1194
            {
1195
                result.push_back(c);
1196
            }
1197
        }
1198
    }
1199
1200
    // flatten
1201
1202
    template <typename Json>
1203
    void flatten_(const std::basic_string<typename Json::char_type>& parent_key,
1204
                  const Json& parent_value,
1205
                  Json& result)
1206
    {
1207
        using char_type = typename Json::char_type;
1208
        using string_type = std::basic_string<char_type>;
1209
1210
        switch (parent_value.type())
1211
        {
1212
            case json_type::array_value:
1213
            {
1214
                if (parent_value.empty())
1215
                {
1216
                    // Flatten empty array to null
1217
                    //result.try_emplace(parent_key, null_type{});
1218
                    //result[parent_key] = parent_value;
1219
                    result.try_emplace(parent_key, parent_value);
1220
                }
1221
                else
1222
                {
1223
                    for (std::size_t i = 0; i < parent_value.size(); ++i)
1224
                    {
1225
                        string_type key(parent_key);
1226
                        key.push_back('/');
1227
                        jsoncons::utility::from_integer(i,key);
1228
                        flatten_(key, parent_value.at(i), result);
1229
                    }
1230
                }
1231
                break;
1232
            }
1233
1234
            case json_type::object_value:
1235
            {
1236
                if (parent_value.empty())
1237
                {
1238
                    // Flatten empty object to null
1239
                    //result.try_emplace(parent_key, null_type{});
1240
                    //result[parent_key] = parent_value;
1241
                    result.try_emplace(parent_key, parent_value);
1242
                }
1243
                else
1244
                {
1245
                    for (const auto& item : parent_value.object_range())
1246
                    {
1247
                        string_type key(parent_key);
1248
                        key.push_back('/');
1249
                        escape(jsoncons::basic_string_view<char_type>(item.key().data(),item.key().size()), key);
1250
                        flatten_(key, item.value(), result);
1251
                    }
1252
                }
1253
                break;
1254
            }
1255
1256
            default:
1257
            {
1258
                // add primitive parent_value with its reference string
1259
                //result[parent_key] = parent_value;
1260
                result.try_emplace(parent_key, parent_value);
1261
                break;
1262
            }
1263
        }
1264
    }
1265
1266
    template <typename Json>
1267
    Json flatten(const Json& value)
1268
    {
1269
        Json result;
1270
        std::basic_string<typename Json::char_type> parent_key;
1271
        flatten_(parent_key, value, result);
1272
        return result;
1273
    }
1274
1275
1276
    // unflatten
1277
1278
    enum class unflatten_options {none,assume_object = 1
1279
};
1280
1281
    template <typename Json>
1282
    Json safe_unflatten (Json& value)
1283
    {
1284
        if (!value.is_object() || value.empty())
1285
        {
1286
            return value;
1287
        }
1288
        bool safe = true;
1289
        std::size_t index = 0;
1290
        for (const auto& item : value.object_range())
1291
        {
1292
            std::size_t n;
1293
            auto r = jsoncons::utility::dec_to_integer(item.key().data(),item.key().size(), n);
1294
            if (!r || (index++ != n))
1295
            {
1296
                safe = false;
1297
                break;
1298
            }
1299
        }
1300
1301
        if (safe)
1302
        {
1303
            Json j(json_array_arg);
1304
            j.reserve(value.size());
1305
            for (auto& item : value.object_range())
1306
            {
1307
                j.emplace_back(std::move(item.value()));
1308
            }
1309
            Json a(json_array_arg);
1310
            for (auto& item : j.array_range())
1311
            {
1312
                a.emplace_back(safe_unflatten (item));
1313
            }
1314
            return a;
1315
        }
1316
        else
1317
        {
1318
            Json o(json_object_arg);
1319
            for (auto& item : value.object_range())
1320
            {
1321
                o.try_emplace(item.key(), safe_unflatten (item.value()));
1322
            }
1323
            return o;
1324
        }
1325
    }
1326
1327
    template <typename Json>
1328
    jsoncons::optional<Json> try_unflatten_array(const Json& value)
1329
    {
1330
        using char_type = typename Json::char_type;
1331
1332
        if (JSONCONS_UNLIKELY(!value.is_object()))
1333
        {
1334
            JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid));
1335
        }
1336
        Json result;
1337
1338
        for (const auto& item: value.object_range())
1339
        {
1340
            Json* part = &result;
1341
            basic_json_pointer<char_type> ptr(item.key());
1342
            std::size_t index = 0;
1343
            for (auto it = ptr.begin(); it != ptr.end(); )
1344
            {
1345
                auto s = *it;
1346
                std::size_t n{0};
1347
                auto r = jsoncons::utility::dec_to_integer(s.data(), s.size(), n);
1348
                if (r.ec == std::errc() && (index++ == n))
1349
                {
1350
                    if (!part->is_array())
1351
                    {
1352
                        *part = Json(json_array_arg);
1353
                    }
1354
                    if (++it != ptr.end())
1355
                    {
1356
                        if (n+1 > part->size())
1357
                        {
1358
                            Json& ref = part->emplace_back();
1359
                            part = std::addressof(ref);
1360
                        }
1361
                        else
1362
                        {
1363
                            part = &part->at(n);
1364
                        }
1365
                    }
1366
                    else
1367
                    {
1368
                        Json& ref = part->emplace_back(item.value());
1369
                        part = std::addressof(ref);
1370
                    }
1371
                }
1372
                else if (part->is_object())
1373
                {
1374
                    if (++it != ptr.end())
1375
                    {
1376
                        auto res = part->try_emplace(s,Json());
1377
                        part = &(res.first->value());
1378
                    }
1379
                    else
1380
                    {
1381
                        auto res = part->try_emplace(s, item.value());
1382
                        part = &(res.first->value());
1383
                    }
1384
                }
1385
                else 
1386
                {
1387
                    return jsoncons::optional<Json>();
1388
                }
1389
            }
1390
        }
1391
1392
        return result;
1393
    }
1394
1395
    template <typename Json>
1396
    Json unflatten_to_object(const Json& value, unflatten_options options = unflatten_options::none)
1397
    {
1398
        using char_type = typename Json::char_type;
1399
1400
        if (JSONCONS_UNLIKELY(!value.is_object()))
1401
        {
1402
            JSONCONS_THROW(jsonpointer_error(jsonpointer_errc::argument_to_unflatten_invalid));
1403
        }
1404
        Json result;
1405
1406
        for (const auto& item: value.object_range())
1407
        {
1408
            Json* part = &result;
1409
            basic_json_pointer<char_type> ptr(item.key());
1410
            for (auto it = ptr.begin(); it != ptr.end(); )
1411
            {
1412
                auto s = *it;
1413
                if (++it != ptr.end())
1414
                {
1415
                    auto res = part->try_emplace(s,Json());
1416
                    part = &(res.first->value());
1417
                }
1418
                else
1419
                {
1420
                    auto res = part->try_emplace(s, item.value());
1421
                    part = &(res.first->value());
1422
                }
1423
            }
1424
        }
1425
1426
        return options == unflatten_options::none ? safe_unflatten (result) : result;
1427
    }
1428
1429
    template <typename Json>
1430
    Json unflatten(const Json& value, unflatten_options options = unflatten_options::none)
1431
    {
1432
        if (options == unflatten_options::none)
1433
        {
1434
            jsoncons::optional<Json> j = try_unflatten_array(value);
1435
            return j ? *j : unflatten_to_object(value,options);
1436
        }
1437
        else
1438
        {
1439
            return unflatten_to_object(value,options);
1440
        }
1441
    }
1442
1443
} // namespace jsonpointer
1444
} // namespace jsoncons
1445
1446
namespace std {
1447
    template <typename CharT>
1448
    struct hash<jsoncons::jsonpointer::basic_json_pointer<CharT>>
1449
    {
1450
        std::size_t operator()(const jsoncons::jsonpointer::basic_json_pointer<CharT>& ptr) const noexcept
1451
        {
1452
            constexpr std::uint64_t prime{0x100000001B3};
1453
            std::uint64_t result{0xcbf29ce484222325};
1454
             
1455
            for (const auto& str : ptr)
1456
            {
1457
                for (std::size_t i = 0; i < str.length(); ++i)
1458
                {
1459
                    result = (result * prime) ^ str[i];
1460
                }
1461
            }
1462
            return result;
1463
        }
1464
    };   
1465
    
1466
} // namespace std
1467
1468
#endif