Coverage Report

Created: 2023-09-25 06:13

/src/msgpack-c/include/msgpack/v1/vrefbuffer.hpp
Line
Count
Source (jump to first uncovered line)
1
//
2
// MessagePack for C++ zero-copy buffer implementation
3
//
4
// Copyright (C) 2008-2017 FURUHASHI Sadayuki and KONDO Takatoshi
5
//
6
//    Distributed under the Boost Software License, Version 1.0.
7
//    (See accompanying file LICENSE_1_0.txt or copy at
8
//    http://www.boost.org/LICENSE_1_0.txt)
9
//
10
#ifndef MSGPACK_V1_VREFBUFFER_HPP
11
#define MSGPACK_V1_VREFBUFFER_HPP
12
13
#include "msgpack/v1/vrefbuffer_decl.hpp"
14
#include "msgpack/assert.hpp"
15
16
#include <stdexcept>
17
#include <algorithm>
18
19
#if defined(_MSC_VER)
20
// avoiding confliction std::max, std::min, and macro in windows.h
21
#ifndef NOMINMAX
22
#define NOMINMAX
23
#endif
24
#endif // defined(_MSC_VER)
25
26
#if defined(unix) || defined(__unix) || defined(__APPLE__) || defined(__OpenBSD__)
27
#include <sys/uio.h>
28
namespace msgpack {
29
typedef ::iovec iovec;
30
} // namespace msgpack
31
#else
32
namespace msgpack {
33
struct iovec {
34
    void  *iov_base;
35
    size_t iov_len;
36
};
37
} // namespace msgpack
38
#endif
39
40
namespace msgpack {
41
42
/// @cond
43
MSGPACK_API_VERSION_NAMESPACE(v1) {
44
/// @endcond
45
46
namespace detail {
47
    // int64, uint64, double
48
    std::size_t const packer_max_buffer_size = 9;
49
} // detail
50
51
class vrefbuffer {
52
private:
53
    struct chunk {
54
        chunk* next;
55
    };
56
    struct inner_buffer {
57
        size_t free;
58
        char*  ptr;
59
        chunk* head;
60
    };
61
public:
62
    vrefbuffer(size_t ref_size = MSGPACK_VREFBUFFER_REF_SIZE,
63
               size_t chunk_size = MSGPACK_VREFBUFFER_CHUNK_SIZE)
64
        :m_ref_size(std::max(ref_size, detail::packer_max_buffer_size + 1)),
65
         m_chunk_size(chunk_size)
66
0
    {
67
0
        if((sizeof(chunk) + chunk_size) < chunk_size) {
68
0
            throw std::bad_alloc();
69
0
        }
70
0
71
0
        size_t nfirst = (sizeof(iovec) < 72/2) ?
72
0
            72 / sizeof(iovec) : 8;
73
0
74
0
        iovec* array = static_cast<iovec*>(::malloc(
75
0
            sizeof(iovec) * nfirst));
76
0
        if(!array) {
77
0
            throw std::bad_alloc();
78
0
        }
79
0
80
0
        m_tail  = array;
81
0
        m_end   = array + nfirst;
82
0
        m_array = array;
83
0
84
0
        chunk* c = static_cast<chunk*>(::malloc(sizeof(chunk) + chunk_size));
85
0
        if(!c) {
86
0
            ::free(array);
87
0
            throw std::bad_alloc();
88
0
        }
89
0
        inner_buffer* const ib = &m_inner_buffer;
90
0
91
0
        ib->free = chunk_size;
92
0
        ib->ptr      = reinterpret_cast<char*>(c) + sizeof(chunk);
93
0
        ib->head = c;
94
0
        c->next = MSGPACK_NULLPTR;
95
0
96
0
    }
97
98
    ~vrefbuffer()
99
0
    {
100
0
        chunk* c = m_inner_buffer.head;
101
0
        while(true) {
102
0
            chunk* n = c->next;
103
0
            ::free(c);
104
0
            if(n != NULL) {
105
0
                c = n;
106
0
            } else {
107
0
                break;
108
0
            }
109
0
        }
110
0
        ::free(m_array);
111
0
    }
112
113
public:
114
    void write(const char* buf, size_t len)
115
0
    {
116
0
        MSGPACK_ASSERT(buf || len == 0);
117
0
118
0
        if (!buf) return;
119
0
120
0
        if(len < m_ref_size) {
121
0
            append_copy(buf, len);
122
0
        } else {
123
0
            append_ref(buf, len);
124
0
        }
125
0
    }
126
127
    void append_ref(const char* buf, size_t len)
128
0
    {
129
0
        if(m_tail == m_end) {
130
0
            const size_t nused = static_cast<size_t>(m_tail - m_array);
131
0
            const size_t nnext = nused * 2;
132
0
133
0
            iovec* nvec = static_cast<iovec*>(::realloc(
134
0
                m_array, sizeof(iovec)*nnext));
135
0
            if(!nvec) {
136
0
                throw std::bad_alloc();
137
0
            }
138
0
139
0
            m_array = nvec;
140
0
            m_end   = nvec + nnext;
141
0
            m_tail  = nvec + nused;
142
0
        }
143
0
144
0
        m_tail->iov_base = const_cast<char*>(buf);
145
0
        m_tail->iov_len     = len;
146
0
        ++m_tail;
147
0
    }
148
149
    void append_copy(const char* buf, size_t len)
150
0
    {
151
0
        inner_buffer* const ib = &m_inner_buffer;
152
0
153
0
        if(ib->free < len) {
154
0
            size_t sz = m_chunk_size;
155
0
            if(sz < len) {
156
0
                sz = len;
157
0
            }
158
0
159
0
            if(sizeof(chunk) + sz < sz){
160
0
                throw std::bad_alloc();
161
0
            }
162
0
163
0
            chunk* c = static_cast<chunk*>(::malloc(sizeof(chunk) + sz));
164
0
            if(!c) {
165
0
                throw std::bad_alloc();
166
0
            }
167
0
168
0
            c->next = ib->head;
169
0
            ib->head = c;
170
0
            ib->free = sz;
171
0
            ib->ptr      = reinterpret_cast<char*>(c) + sizeof(chunk);
172
0
        }
173
0
174
0
        char* m = ib->ptr;
175
0
        std::memcpy(m, buf, len);
176
0
        ib->free -= len;
177
0
        ib->ptr      += len;
178
0
179
0
        if(m_tail != m_array && m ==
180
0
            static_cast<const char*>(
181
0
                const_cast<const void *>((m_tail - 1)->iov_base)
182
0
            ) + (m_tail - 1)->iov_len) {
183
0
            (m_tail - 1)->iov_len += len;
184
0
            return;
185
0
        } else {
186
0
            append_ref( m, len);
187
0
        }
188
0
    }
189
190
    const iovec* vector() const
191
0
    {
192
0
        return m_array;
193
0
    }
194
195
    size_t vector_size() const
196
0
    {
197
0
        return static_cast<size_t>(m_tail - m_array);
198
0
    }
199
200
    void migrate(vrefbuffer* to)
201
0
    {
202
0
        size_t sz = m_chunk_size;
203
0
204
0
        if((sizeof(chunk) + sz) < sz){
205
0
            throw std::bad_alloc();
206
0
        }
207
0
208
0
        chunk* empty = static_cast<chunk*>(::malloc(sizeof(chunk) + sz));
209
0
        if(!empty) {
210
0
            throw std::bad_alloc();
211
0
        }
212
0
213
0
        empty->next = MSGPACK_NULLPTR;
214
0
215
0
        const size_t nused = static_cast<size_t>(m_tail - m_array);
216
0
        if(to->m_tail + nused < m_end) {
217
0
            const size_t tosize = static_cast<size_t>(to->m_tail - to->m_array);
218
0
            const size_t reqsize = nused + tosize;
219
0
            size_t nnext = static_cast<size_t>(to->m_end - to->m_array) * 2;
220
0
            while(nnext < reqsize) {
221
0
                size_t tmp_nnext = nnext * 2;
222
0
                if (tmp_nnext <= nnext) {
223
0
                    nnext = reqsize;
224
0
                    break;
225
0
                }
226
0
                nnext = tmp_nnext;
227
0
            }
228
0
229
0
            iovec* nvec = static_cast<iovec*>(::realloc(
230
0
                to->m_array, sizeof(iovec)*nnext));
231
0
            if(!nvec) {
232
0
                ::free(empty);
233
0
                throw std::bad_alloc();
234
0
            }
235
0
236
0
            to->m_array = nvec;
237
0
            to->m_end   = nvec + nnext;
238
0
            to->m_tail  = nvec + tosize;
239
0
        }
240
0
241
0
        std::memcpy(to->m_tail, m_array, sizeof(iovec)*nused);
242
0
243
0
        to->m_tail += nused;
244
0
        m_tail = m_array;
245
0
246
0
247
0
        inner_buffer* const ib = &m_inner_buffer;
248
0
        inner_buffer* const toib = &to->m_inner_buffer;
249
0
250
0
        chunk* last = ib->head;
251
0
        while(last->next) {
252
0
            last = last->next;
253
0
        }
254
0
        last->next = toib->head;
255
0
        toib->head = ib->head;
256
0
257
0
        if(toib->free < ib->free) {
258
0
            toib->free = ib->free;
259
0
            toib->ptr  = ib->ptr;
260
0
        }
261
0
262
0
        ib->head = empty;
263
0
        ib->free = sz;
264
0
        ib->ptr      = reinterpret_cast<char*>(empty) + sizeof(chunk);
265
0
266
0
    }
267
268
    void clear()
269
0
    {
270
0
        chunk* c = m_inner_buffer.head->next;
271
0
        chunk* n;
272
0
        while(c) {
273
0
            n = c->next;
274
0
            ::free(c);
275
0
            c = n;
276
0
        }
277
0
278
0
        inner_buffer* const ib = &m_inner_buffer;
279
0
        c = ib->head;
280
0
        c->next = MSGPACK_NULLPTR;
281
0
        ib->free = m_chunk_size;
282
0
        ib->ptr      = reinterpret_cast<char*>(c) + sizeof(chunk);
283
0
284
0
        m_tail = m_array;
285
0
    }
286
287
#if defined(MSGPACK_USE_CPP03)
288
private:
289
    vrefbuffer(const vrefbuffer&);
290
    vrefbuffer& operator=(const vrefbuffer&);
291
#else  // defined(MSGPACK_USE_CPP03)
292
    vrefbuffer(const vrefbuffer&) = delete;
293
    vrefbuffer& operator=(const vrefbuffer&) = delete;
294
#endif // defined(MSGPACK_USE_CPP03)
295
296
private:
297
    iovec* m_tail;
298
    iovec* m_end;
299
    iovec* m_array;
300
301
    size_t m_ref_size;
302
    size_t m_chunk_size;
303
304
    inner_buffer m_inner_buffer;
305
306
};
307
308
/// @cond
309
}  // MSGPACK_API_VERSION_NAMESPACE(v1)
310
/// @endcond
311
312
}  // namespace msgpack
313
314
#endif // MSGPACK_V1_VREFBUFFER_HPP