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 -
#include <boost/core/detail/static_assert.hpp>
 
14  

13  

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

18  

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

22  

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

32  

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

39  

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

45  

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

51  

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

63  

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

70  

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

75  

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

91  

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

97  

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

108  

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

111  

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

114  

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

120  

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

126  

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

133  

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

139  

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

145  

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

151  

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

154  

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

158  

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

161  

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

166  

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

171  

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

177  

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

184  

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

188  

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