1  
//
1  
//
2  
// Copyright (c) 2023 Dmitry Arkhipov (grisumbras@yandex.ru)
2  
// Copyright (c) 2023 Dmitry Arkhipov (grisumbras@yandex.ru)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/boostorg/json
7  
// Official repository: https://github.com/boostorg/json
8  
//
8  
//
9  

9  

10  

10  

11  
#ifndef BOOST_JSON_DETAIL_SBO_BUFFER_HPP
11  
#ifndef BOOST_JSON_DETAIL_SBO_BUFFER_HPP
12  
#define BOOST_JSON_DETAIL_SBO_BUFFER_HPP
12  
#define BOOST_JSON_DETAIL_SBO_BUFFER_HPP
13  

13  

14  
#include <boost/core/detail/static_assert.hpp>
14  
#include <boost/core/detail/static_assert.hpp>
15  
#include <boost/json/detail/config.hpp>
15  
#include <boost/json/detail/config.hpp>
16  
#include <boost/json/detail/except.hpp>
16  
#include <boost/json/detail/except.hpp>
17  
#include <string>
17  
#include <string>
18  
#include <array>
18  
#include <array>
19  

19  

20  
namespace boost {
20  
namespace boost {
21  
namespace json {
21  
namespace json {
22  
namespace detail {
22  
namespace detail {
23  

23  

24  
template< std::size_t N >
24  
template< std::size_t N >
25  
class sbo_buffer
25  
class sbo_buffer
26  
{
26  
{
27  
    struct size_ptr_pair
27  
    struct size_ptr_pair
28  
    {
28  
    {
29  
        std::size_t size;
29  
        std::size_t size;
30  
        char* ptr;
30  
        char* ptr;
31  
    };
31  
    };
32  
    BOOST_CORE_STATIC_ASSERT( N >= sizeof(size_ptr_pair) );
32  
    BOOST_CORE_STATIC_ASSERT( N >= sizeof(size_ptr_pair) );
33  

33  

34  
    union {
34  
    union {
35  
        std::array<char, N> buffer_;
35  
        std::array<char, N> buffer_;
36  
        std::size_t capacity_;
36  
        std::size_t capacity_;
37  
    };
37  
    };
38  
    char* data_ = buffer_.data();
38  
    char* data_ = buffer_.data();
39  
    std::size_t size_ = 0;
39  
    std::size_t size_ = 0;
40  

40  

41  
    bool
41  
    bool
42  
    is_small() const noexcept
42  
    is_small() const noexcept
43  
    {
43  
    {
44  
        return data_ == buffer_.data();
44  
        return data_ == buffer_.data();
45  
    }
45  
    }
46  

46  

47  
    void
47  
    void
48  
    dispose()
48  
    dispose()
49  
    {
49  
    {
50  
        if( is_small() )
50  
        if( is_small() )
51  
            return;
51  
            return;
52  

52  

53  
        delete[] data_;
53  
        delete[] data_;
54  
#if defined(__GNUC__)
54  
#if defined(__GNUC__)
55  
# pragma GCC diagnostic push
55  
# pragma GCC diagnostic push
56  
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
56  
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
57  
#endif
57  
#endif
58  
        buffer_ = {};
58  
        buffer_ = {};
59  
#if defined(__GNUC__)
59  
#if defined(__GNUC__)
60  
# pragma GCC diagnostic pop
60  
# pragma GCC diagnostic pop
61  
#endif
61  
#endif
62  
        data_ = buffer_.data();
62  
        data_ = buffer_.data();
63  
    }
63  
    }
64  

64  

65  
    static constexpr
65  
    static constexpr
66  
    std::size_t
66  
    std::size_t
67  
    max_size() noexcept
67  
    max_size() noexcept
68  
    {
68  
    {
69  
        return BOOST_JSON_MAX_STRING_SIZE;
69  
        return BOOST_JSON_MAX_STRING_SIZE;
70  
    }
70  
    }
71  

71  

72  
public:
72  
public:
73  
    sbo_buffer()
73  
    sbo_buffer()
74  
        : buffer_()
74  
        : buffer_()
75  
    {}
75  
    {}
76  

76  

77  
    sbo_buffer( sbo_buffer&& other ) noexcept
77  
    sbo_buffer( sbo_buffer&& other ) noexcept
78  
        : size_(other.size_)
78  
        : size_(other.size_)
79  
    {
79  
    {
80  
        if( other.is_small() )
80  
        if( other.is_small() )
81  
        {
81  
        {
82  
            buffer_ = other.buffer_;
82  
            buffer_ = other.buffer_;
83  
            data_ = buffer_.data();
83  
            data_ = buffer_.data();
84  
        }
84  
        }
85  
        else
85  
        else
86  
        {
86  
        {
87  
            data_ = other.data_;
87  
            data_ = other.data_;
88  
            other.data_ = other.buffer_.data();
88  
            other.data_ = other.buffer_.data();
89  
        }
89  
        }
90  
        BOOST_ASSERT( other.is_small() );
90  
        BOOST_ASSERT( other.is_small() );
91  
    }
91  
    }
92  

92  

93  
    sbo_buffer&
93  
    sbo_buffer&
94  
    operator=( sbo_buffer&& other ) noexcept
94  
    operator=( sbo_buffer&& other ) noexcept
95  
    {
95  
    {
96  
        if( &other == this )
96  
        if( &other == this )
97  
            return this;
97  
            return this;
98  

98  

99  
        if( other.is_small() )
99  
        if( other.is_small() )
100  
        {
100  
        {
101  
            buffer_ = other.buffer_;
101  
            buffer_ = other.buffer_;
102  
            data_ = buffer_.data();
102  
            data_ = buffer_.data();
103  
        }
103  
        }
104  
        else
104  
        else
105  
        {
105  
        {
106  
            data_ = other.data_;
106  
            data_ = other.data_;
107  
            other.data_ = other.buffer_.data();
107  
            other.data_ = other.buffer_.data();
108  
        }
108  
        }
109  

109  

110  
        size_ = other.size_;
110  
        size_ = other.size_;
111  
        other.size_ = 0;
111  
        other.size_ = 0;
112  

112  

113  
        return *this;
113  
        return *this;
114  
    }
114  
    }
115  

115  

116  
    ~sbo_buffer()
116  
    ~sbo_buffer()
117  
    {
117  
    {
118  
        if( !is_small() )
118  
        if( !is_small() )
119  
            delete[] data_;
119  
            delete[] data_;
120  
    }
120  
    }
121  

121  

122  
    std::size_t
122  
    std::size_t
123  
    capacity() const noexcept
123  
    capacity() const noexcept
124  
    {
124  
    {
125  
        return is_small() ? buffer_.size() : capacity_;
125  
        return is_small() ? buffer_.size() : capacity_;
126  
    }
126  
    }
127  

127  

128  
    void
128  
    void
129  
    reset() noexcept
129  
    reset() noexcept
130  
    {
130  
    {
131  
        dispose();
131  
        dispose();
132  
        clear();
132  
        clear();
133  
    }
133  
    }
134  

134  

135  
    void
135  
    void
136  
    clear()
136  
    clear()
137  
    {
137  
    {
138  
        size_ = 0;
138  
        size_ = 0;
139  
    }
139  
    }
140  

140  

141  
    void
141  
    void
142  
    grow( std::size_t size )
142  
    grow( std::size_t size )
143  
    {
143  
    {
144  
        if( !size )
144  
        if( !size )
145  
            return;
145  
            return;
146  

146  

147  
        if( max_size() - size_ < size )
147  
        if( max_size() - size_ < size )
148  
        {
148  
        {
149  
            BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
149  
            BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
150  
            detail::throw_system_error( error::number_too_large, &loc );
150  
            detail::throw_system_error( error::number_too_large, &loc );
151  
        }
151  
        }
152  

152  

153  
        std::size_t const old_capacity = this->capacity();
153  
        std::size_t const old_capacity = this->capacity();
154  
        std::size_t new_capacity = size_ + size;
154  
        std::size_t new_capacity = size_ + size;
155  

155  

156  
        // growth factor 2
156  
        // growth factor 2
157  
        if( old_capacity <= max_size() - old_capacity ) // check for overflow
157  
        if( old_capacity <= max_size() - old_capacity ) // check for overflow
158  
            new_capacity = (std::max)(old_capacity * 2, new_capacity);
158  
            new_capacity = (std::max)(old_capacity * 2, new_capacity);
159  

159  

160  
        char* new_data = new char[new_capacity];
160  
        char* new_data = new char[new_capacity];
161  
        std::memcpy(new_data, data_, size_);
161  
        std::memcpy(new_data, data_, size_);
162  

162  

163  
        dispose();
163  
        dispose();
164  
        data_ = new_data;
164  
        data_ = new_data;
165  
        capacity_ = new_capacity;
165  
        capacity_ = new_capacity;
166  
    }
166  
    }
167  

167  

168  
    char*
168  
    char*
169  
    append( char const* ptr, std::size_t size )
169  
    append( char const* ptr, std::size_t size )
170  
    {
170  
    {
171  
        grow(size);
171  
        grow(size);
172  

172  

173  
        if(BOOST_JSON_LIKELY( size ))
173  
        if(BOOST_JSON_LIKELY( size ))
174  
            std::memcpy( data_ + size_, ptr, size );
174  
            std::memcpy( data_ + size_, ptr, size );
175  
        size_ += size;
175  
        size_ += size;
176  
        return data_;
176  
        return data_;
177  
    }
177  
    }
178  

178  

179  
    std::size_t
179  
    std::size_t
180  
    size() noexcept
180  
    size() noexcept
181  
    {
181  
    {
182  
        return size_;
182  
        return size_;
183  
    }
183  
    }
184  
};
184  
};
185  

185  

186  
} // namespace detail
186  
} // namespace detail
187  
} // namespace json
187  
} // namespace json
188  
} // namespace boost
188  
} // namespace boost
189  

189  

190  
#endif // BOOST_JSON_DETAIL_SBO_BUFFER_HPP
190  
#endif // BOOST_JSON_DETAIL_SBO_BUFFER_HPP