Coverage Report

Created: 2025-11-24 06:07

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