Coverage Report

Created: 2026-01-09 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/brpc/src/butil/iobuf_inl.h
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
// iobuf - A non-continuous zero-copied buffer
19
20
// Date: Thu Nov 22 13:57:56 CST 2012
21
22
// Inlined implementations of some methods defined in iobuf.h
23
24
#ifndef BUTIL_IOBUF_INL_H
25
#define BUTIL_IOBUF_INL_H
26
27
#include "butil/atomicops.h"                // butil::atomic
28
#include "butil/thread_local.h"             // thread_atexit
29
#include "butil/logging.h"                  // CHECK, LOG
30
31
void* fast_memcpy(void *__restrict dest, const void *__restrict src, size_t n);
32
33
namespace butil {
34
35
using UserDataDeleter = std::function<void(void*)>;
36
37
struct UserDataExtension {
38
    UserDataDeleter deleter;
39
};
40
41
bool IsIOBufProfilerSamplable();
42
void SubmitIOBufSample(IOBuf::Block* block, int64_t ref);
43
44
const uint16_t IOBUF_BLOCK_FLAGS_USER_DATA = 1 << 0;
45
const uint16_t IOBUF_BLOCK_FLAGS_SAMPLED = 1 << 1;
46
47
0
inline ssize_t IOBuf::cut_into_file_descriptor(int fd, size_t size_hint) {
48
0
    return pcut_into_file_descriptor(fd, -1, size_hint);
49
0
}
50
51
inline ssize_t IOBuf::cut_multiple_into_file_descriptor(
52
0
    int fd, IOBuf* const* pieces, size_t count) {
53
0
    return pcut_multiple_into_file_descriptor(fd, -1, pieces, count);
54
0
}
55
56
0
inline int IOBuf::append_user_data(void* data, size_t size, std::function<void(void*)> deleter) {
57
0
    return append_user_data_with_meta(data, size, std::move(deleter), 0);
58
0
}
59
60
0
inline ssize_t IOPortal::append_from_file_descriptor(int fd, size_t max_count) {
61
0
    return pappend_from_file_descriptor(fd, -1, max_count);
62
0
}
63
64
0
inline void IOPortal::return_cached_blocks() {
65
0
    if (_block) {
66
0
        return_cached_blocks_impl(_block);
67
0
        _block = NULL;
68
0
    }
69
0
}
70
71
2.06k
inline void reset_block_ref(IOBuf::BlockRef& ref) {
72
2.06k
    ref.offset = 0;
73
2.06k
    ref.length = 0;
74
2.06k
    ref.block = NULL;
75
2.06k
}
76
77
685
inline IOBuf::IOBuf() {
78
685
    reset_block_ref(_sv.refs[0]);
79
685
    reset_block_ref(_sv.refs[1]);
80
685
}
81
82
inline IOBuf::IOBuf(const Movable& rhs) {
83
    _sv = rhs.value()._sv;
84
    new (&rhs.value()) IOBuf;
85
}
86
87
0
inline void IOBuf::operator=(const Movable& rhs) {
88
0
    clear();
89
0
    _sv = rhs.value()._sv;
90
0
    new (&rhs.value()) IOBuf;
91
0
}
92
93
0
inline void IOBuf::operator=(const char* s) {
94
0
    clear();
95
0
    append(s);
96
0
}
97
98
0
inline void IOBuf::operator=(const std::string& s) {
99
0
    clear();
100
0
    append(s);
101
0
}
102
103
0
inline void IOBuf::swap(IOBuf& other) {
104
0
    const SmallView tmp = other._sv;
105
0
    other._sv = _sv;
106
0
    _sv = tmp;
107
0
}
108
109
0
inline int IOBuf::cut_until(IOBuf* out, char const* delim) {
110
0
    if (*delim) {
111
0
        if (!*(delim+1)) {
112
0
            return _cut_by_char(out, *delim);
113
0
        } else {
114
0
            return _cut_by_delim(out, delim, strlen(delim));
115
0
        }
116
0
    }
117
0
    return -1;
118
0
}
119
120
0
inline int IOBuf::cut_until(IOBuf* out, const std::string& delim) {
121
0
    if (delim.length() == 1UL) {
122
0
        return _cut_by_char(out, delim[0]);
123
0
    } else if (delim.length() > 1UL) {
124
0
        return _cut_by_delim(out, delim.data(), delim.length());
125
0
    } else {
126
0
        return -1;
127
0
    }
128
0
}
129
130
685
inline int IOBuf::append(const std::string& s) {
131
685
    return append(s.data(), s.length());
132
685
}
133
134
0
inline std::string IOBuf::to_string() const {
135
0
    std::string s;
136
0
    copy_to(&s);
137
0
    return s;
138
0
}
139
140
0
inline bool IOBuf::empty() const {
141
0
    return _small() ? !_sv.refs[0].block : !_bv.nbytes;
142
0
}
143
144
972
inline size_t IOBuf::length() const {
145
972
    return _small() ?
146
972
        (_sv.refs[0].length + _sv.refs[1].length) : _bv.nbytes;
147
972
}
148
149
4.23k
inline bool IOBuf::_small() const {
150
4.23k
    return _bv.magic >= 0;
151
4.23k
}
152
153
692
inline size_t IOBuf::_ref_num() const {
154
692
    return _small()
155
692
        ? (!!_sv.refs[0].block + !!_sv.refs[1].block) : _bv.nref;
156
692
}
157
158
208
inline IOBuf::BlockRef& IOBuf::_front_ref() {
159
208
    return _small() ? _sv.refs[0] : _bv.refs[_bv.start];
160
208
}
161
162
0
inline const IOBuf::BlockRef& IOBuf::_front_ref() const {
163
0
    return _small() ? _sv.refs[0] : _bv.refs[_bv.start];
164
0
}
165
166
0
inline IOBuf::BlockRef& IOBuf::_back_ref() {
167
0
    return _small() ? _sv.refs[!!_sv.refs[1].block] : _bv.ref_at(_bv.nref - 1);
168
0
}
169
170
0
inline const IOBuf::BlockRef& IOBuf::_back_ref() const {
171
0
    return _small() ? _sv.refs[!!_sv.refs[1].block] : _bv.ref_at(_bv.nref - 1);
172
0
}
173
174
0
inline IOBuf::BlockRef& IOBuf::_ref_at(size_t i) {
175
0
    return _small() ? _sv.refs[i] : _bv.ref_at(i);
176
0
}
177
178
692
inline const IOBuf::BlockRef& IOBuf::_ref_at(size_t i) const {
179
692
    return _small() ? _sv.refs[i] : _bv.ref_at(i);
180
692
}
181
182
0
inline const IOBuf::BlockRef* IOBuf::_pref_at(size_t i) const {
183
0
    if (_small()) {
184
0
        return i < (size_t)(!!_sv.refs[0].block + !!_sv.refs[1].block) ? &_sv.refs[i] : NULL;
185
0
    } else {
186
0
        return i < _bv.nref ? &_bv.ref_at(i) : NULL;
187
0
    }
188
0
}
189
190
0
inline bool operator==(const IOBuf::BlockRef& r1, const IOBuf::BlockRef& r2) {
191
0
    return r1.offset == r2.offset && r1.length == r2.length &&
192
0
        r1.block == r2.block;
193
0
}
194
        
195
0
inline bool operator!=(const IOBuf::BlockRef& r1, const IOBuf::BlockRef& r2) {
196
0
    return !(r1 == r2);
197
0
}
198
199
693
inline void IOBuf::_push_back_ref(const BlockRef& r) {
200
693
    if (_small()) {
201
693
        return _push_or_move_back_ref_to_smallview<false>(r);
202
693
    } else {
203
0
        return _push_or_move_back_ref_to_bigview<false>(r);
204
0
    }
205
693
}
206
207
0
inline void IOBuf::_move_back_ref(const BlockRef& r) {
208
0
    if (_small()) {
209
0
        return _push_or_move_back_ref_to_smallview<true>(r);
210
0
    } else {
211
0
        return _push_or_move_back_ref_to_bigview<true>(r);
212
0
    }
213
0
}
214
215
////////////////  IOBufCutter ////////////////
216
0
inline size_t IOBufCutter::remaining_bytes() const {
217
0
    if (_block) {
218
0
        return (char*)_data_end - (char*)_data + _buf->size() - _buf->_front_ref().length;
219
0
    } else {
220
0
        return _buf->size();
221
0
    }
222
0
}
223
224
0
inline bool IOBufCutter::cut1(void* c) {
225
0
    if (_data == _data_end) {
226
0
        if (!load_next_ref()) {
227
0
            return false;
228
0
        }
229
0
    }
230
0
    *(char*)c = *(const char*)_data;
231
0
    _data = (char*)_data + 1;
232
0
    return true;
233
0
}
234
235
0
inline const void* IOBufCutter::fetch1() {
236
0
    if (_data == _data_end) {
237
0
        if (!load_next_ref()) {
238
0
            return NULL;
239
0
        }
240
0
    }
241
0
    return _data;
242
0
}
243
244
0
inline size_t IOBufCutter::copy_to(void* out, size_t n) {
245
0
    size_t size = (char*)_data_end - (char*)_data;
246
0
    if (n <= size) {
247
0
        memcpy(out, _data, n);
248
0
        return n;
249
0
    }
250
0
    return slower_copy_to(out, n);
251
0
}
252
253
0
inline size_t IOBufCutter::pop_front(size_t n) {
254
0
    const size_t saved_n = n;
255
0
    do {
256
0
        const size_t size = (char*)_data_end - (char*)_data;
257
0
        if (n <= size) {
258
0
            _data = (char*)_data + n;
259
0
            return saved_n;
260
0
        }
261
0
        n -= size;
262
0
        if (!load_next_ref()) {
263
0
            return saved_n - n;
264
0
        }
265
0
    } while (true);
266
0
}
267
268
0
inline size_t IOBufCutter::cutn(std::string* out, size_t n) {
269
0
    if (n == 0) {
270
0
        return 0;
271
0
    }
272
0
    const size_t len = remaining_bytes();
273
0
    if (n > len) {
274
0
        n = len;
275
0
    }
276
0
    const size_t old_size = out->size();
277
0
    out->resize(out->size() + n);
278
0
    return cutn(&(*out)[old_size], n);
279
0
}
280
281
/////////////// IOBufAppender /////////////////
282
0
inline int IOBufAppender::append(const void* src, size_t n) {
283
0
    do {
284
0
        const size_t size = (char*)_data_end - (char*)_data;
285
0
        if (n <= size) {
286
0
            memcpy(_data, src, n);
287
0
            _data = (char*)_data + n;
288
0
            return 0;
289
0
        }
290
0
        if (size != 0) {
291
0
            memcpy(_data, src, size);
292
0
            src = (const char*)src + size;
293
0
            n -= size;
294
0
        }
295
0
        if (add_block() != 0) {
296
0
            return -1;
297
0
        }
298
0
    } while (true);
299
0
}
300
301
0
inline int IOBufAppender::append(const StringPiece& str) {
302
0
    return append(str.data(), str.size());
303
0
}
304
305
0
inline int IOBufAppender::append_decimal(long d) {
306
0
    char buf[24];  // enough for decimal 64-bit integers
307
0
    size_t n = sizeof(buf);
308
0
    bool negative = false;
309
0
    if (d < 0) {
310
0
        negative = true;
311
0
        d = -d;
312
0
    }
313
0
    do {
314
0
        const long q = d / 10;
315
0
        buf[--n] = d - q * 10 + '0';
316
0
        d = q;
317
0
    } while (d);
318
0
    if (negative) {
319
0
        buf[--n] = '-';
320
0
    }
321
0
    return append(buf + n, sizeof(buf) - n);
322
0
}
323
324
0
inline int IOBufAppender::push_back(char c) {
325
0
    if (_data == _data_end) {
326
0
        if (add_block() != 0) {
327
0
            return -1;
328
0
        }
329
0
    }
330
0
    char* const p = (char*)_data;
331
0
    *p = c;
332
0
    _data = p + 1;
333
0
    return 0;
334
0
}
335
336
0
inline int IOBufAppender::add_block() {
337
0
    int size = 0;
338
0
    if (_zc_stream.Next(&_data, &size)) {
339
0
        _data_end = (char*)_data + size;
340
0
        return 0;
341
0
    }
342
0
    _data = NULL;
343
0
    _data_end = NULL;
344
0
    return -1;
345
0
}
346
347
0
inline void IOBufAppender::shrink() {
348
0
    const size_t size = (char*)_data_end - (char*)_data;
349
0
    if (size != 0) {
350
0
        _zc_stream.BackUp(size);
351
0
        _data = NULL;
352
0
        _data_end = NULL;
353
0
    }
354
0
}
355
356
inline IOBufBytesIterator::IOBufBytesIterator(const butil::IOBuf& buf)
357
685
    : _block_begin(NULL), _block_end(NULL), _block_count(0), 
358
685
      _bytes_left(buf.length()), _buf(&buf) {
359
685
    try_next_block();
360
685
}
361
362
inline IOBufBytesIterator::IOBufBytesIterator(const IOBufBytesIterator& it)
363
    : _block_begin(it._block_begin)
364
    , _block_end(it._block_end)
365
    , _block_count(it._block_count)
366
    , _bytes_left(it._bytes_left)
367
    , _buf(it._buf) {
368
}
369
370
inline IOBufBytesIterator::IOBufBytesIterator(
371
    const IOBufBytesIterator& it, size_t bytes_left)
372
    : _block_begin(it._block_begin)
373
    , _block_end(it._block_end)
374
    , _block_count(it._block_count)
375
    , _bytes_left(bytes_left)
376
    , _buf(it._buf) {
377
    //CHECK_LE(_bytes_left, it._bytes_left);
378
    if (_block_end > _block_begin + _bytes_left) {
379
        _block_end = _block_begin + _bytes_left;
380
    }
381
}
382
383
1.17k
inline void IOBufBytesIterator::try_next_block() {
384
1.17k
    if (_bytes_left == 0) {
385
487
        return;
386
487
    }
387
692
    butil::StringPiece s = _buf->backing_block(_block_count++);
388
692
    _block_begin = s.data();
389
692
    _block_end = s.data() + std::min(s.size(), (size_t)_bytes_left);
390
692
}
391
392
48.7k
inline void IOBufBytesIterator::operator++() {
393
48.7k
    ++_block_begin;
394
48.7k
    --_bytes_left;
395
48.7k
    if (_block_begin == _block_end) {
396
464
        try_next_block();
397
464
    }
398
48.7k
}
399
400
271
inline size_t IOBufBytesIterator::copy_and_forward(void* buf, size_t n) {
401
271
    size_t nc = 0;
402
343
    while (nc < n && _bytes_left != 0) {
403
72
        const size_t block_size = _block_end - _block_begin;
404
72
        const size_t to_copy = std::min(block_size, n - nc);
405
72
        memcpy((char*)buf + nc, _block_begin, to_copy);
406
72
        _block_begin += to_copy;
407
72
        _bytes_left -= to_copy;
408
72
        nc += to_copy;
409
72
        if (_block_begin == _block_end) {
410
30
            try_next_block();
411
30
        }
412
72
    }
413
271
    return nc;
414
271
}
415
416
271
inline size_t IOBufBytesIterator::copy_and_forward(std::string* s, size_t n) {
417
271
    bool resized = false;
418
271
    if (s->size() < n) {
419
71
        resized = true;
420
71
        s->resize(n);
421
71
    }
422
271
    const size_t nc = copy_and_forward(const_cast<char*>(s->data()), n);
423
271
    if (nc < n && resized) {
424
0
        s->resize(nc);
425
0
    }
426
271
    return nc;
427
271
}
428
429
0
inline size_t IOBufBytesIterator::forward(size_t n) {
430
0
    size_t nc = 0;
431
0
    while (nc < n && _bytes_left != 0) {
432
0
        const size_t block_size = _block_end - _block_begin;
433
0
        const size_t to_copy = std::min(block_size, n - nc);
434
0
        _block_begin += to_copy;
435
0
        _bytes_left -= to_copy;
436
0
        nc += to_copy;
437
0
        if (_block_begin == _block_end) {
438
0
            try_next_block();
439
0
        }
440
0
    }
441
0
    return nc;
442
0
}
443
444
// Used by max_blocks_per_thread()
445
bool IsIOBufProfilerEnabled();
446
447
namespace iobuf {
448
void inc_g_nblock();
449
void dec_g_nblock();
450
451
void inc_g_blockmem();
452
void dec_g_blockmem();
453
454
void inc_g_num_hit_tls_threshold();
455
void dec_g_num_hit_tls_threshold();
456
457
// Function pointers to allocate or deallocate memory for a IOBuf::Block
458
extern void* (*blockmem_allocate)(size_t);
459
extern void  (*blockmem_deallocate)(void*);
460
461
} // namespace iobuf
462
463
struct IOBuf::Block {
464
    butil::atomic<int> nshared;
465
    uint16_t flags;
466
    uint16_t abi_check;  // original cap, never be zero.
467
    uint32_t size;
468
    uint32_t cap;
469
    // When flag is 0, portal_next is valid.
470
    // When flag & IOBUF_BLOCK_FLAGS_USER_DATA is non-0, data_meta is valid.
471
    union {
472
        Block* portal_next;
473
        uint64_t data_meta;
474
    } u;
475
    // When flag is 0, data points to `size` bytes starting at `(char*)this+sizeof(Block)'
476
    // When flag & IOBUF_BLOCK_FLAGS_USER_DATA is non-0, data points to the user data and
477
    // the deleter is put in UserDataExtension at `(char*)this+sizeof(Block)'
478
    char* data;
479
        
480
    Block(char* data_in, uint32_t data_size)
481
9
        : nshared(1)
482
9
        , flags(0)
483
9
        , abi_check(0)
484
9
        , size(0)
485
9
        , cap(data_size)
486
9
        , u({NULL})
487
9
        , data(data_in) {
488
9
        iobuf::inc_g_nblock();
489
9
        iobuf::inc_g_blockmem();
490
9
        if (is_samplable()) {
491
9
            SubmitIOBufSample(this, 1);
492
9
        }
493
9
    }
494
495
    Block(char* data_in, uint32_t data_size, UserDataDeleter deleter)
496
0
        : nshared(1)
497
0
        , flags(IOBUF_BLOCK_FLAGS_USER_DATA)
498
0
        , abi_check(0)
499
0
        , size(data_size)
500
0
        , cap(data_size)
501
0
        , u({0})
502
0
        , data(data_in) {
503
0
        auto ext = new (get_user_data_extension()) UserDataExtension();
504
0
        ext->deleter = std::move(deleter);
505
0
        if (is_samplable()) {
506
0
            SubmitIOBufSample(this, 1);
507
0
        }
508
0
    }
509
510
    // Undefined behavior when (flags & IOBUF_BLOCK_FLAGS_USER_DATA) is 0.
511
0
    UserDataExtension* get_user_data_extension() {
512
0
        char* p = (char*)this;
513
0
        return (UserDataExtension*)(p + sizeof(Block));
514
0
    }
515
516
1.39k
    inline void check_abi() {
517
1.39k
#ifndef NDEBUG
518
1.39k
    if (abi_check != 0) {
519
0
        LOG(FATAL) << "Your program seems to wrongly contain two "
520
0
            "ABI-incompatible implementations of IOBuf";
521
0
    }
522
1.39k
#endif
523
1.39k
}
524
525
693
    void inc_ref() {
526
693
        check_abi();
527
693
        nshared.fetch_add(1, butil::memory_order_relaxed);
528
693
        if (sampled()) {
529
693
            SubmitIOBufSample(this, 1);
530
693
        }
531
693
    }
532
        
533
701
    void dec_ref() {
534
701
        check_abi();
535
701
        if (sampled()) {
536
701
            SubmitIOBufSample(this, -1);
537
701
        }
538
701
        if (nshared.fetch_sub(1, butil::memory_order_release) == 1) {
539
8
            butil::atomic_thread_fence(butil::memory_order_acquire);
540
8
            if (!is_user_data()) {
541
8
                iobuf::dec_g_nblock();
542
8
                iobuf::dec_g_blockmem();
543
8
                this->~Block();
544
8
                iobuf::blockmem_deallocate(this);
545
8
            } else if (flags & IOBUF_BLOCK_FLAGS_USER_DATA) {
546
0
                auto ext = get_user_data_extension();
547
0
                ext->deleter(data);
548
0
                ext->~UserDataExtension();
549
0
                this->~Block();
550
0
                free(this);
551
0
            }
552
8
        }
553
701
    }
554
555
0
    int ref_count() const {
556
0
        return nshared.load(butil::memory_order_relaxed);
557
0
    }
558
559
700
    bool full() const { return size >= cap; }
560
693
    size_t left_space() const { return cap - size; }
561
562
private:
563
9
    bool is_samplable() {
564
9
        if (IsIOBufProfilerSamplable()) {
565
9
            flags |= IOBUF_BLOCK_FLAGS_SAMPLED;
566
9
            return true;
567
9
        }
568
0
        return false;
569
9
    }
570
571
1.39k
    bool sampled() const {
572
1.39k
        return flags & IOBUF_BLOCK_FLAGS_SAMPLED;
573
1.39k
    }
574
575
8
    bool is_user_data() const {
576
8
        return flags & IOBUF_BLOCK_FLAGS_USER_DATA;
577
8
    }
578
};
579
580
namespace iobuf {
581
struct TLSData {
582
    // Head of the TLS block chain.
583
    IOBuf::Block* block_head;
584
    
585
    // Number of TLS blocks
586
    int num_blocks;
587
    
588
    // True if the remote_tls_block_chain is registered to the thread.
589
    bool registered;
590
};
591
592
// Max number of blocks in each TLS. This is a soft limit namely
593
// release_tls_block_chain() may exceed this limit sometimes.
594
const int MAX_BLOCKS_PER_THREAD = 8;
595
596
0
inline int max_blocks_per_thread() {
597
    // If IOBufProfiler is enabled, do not cache blocks in TLS.
598
0
    return IsIOBufProfilerEnabled() ? 0 : MAX_BLOCKS_PER_THREAD;
599
0
}
600
601
TLSData* get_g_tls_data();
602
void remove_tls_block_chain();
603
604
IOBuf::Block* acquire_tls_block();
605
606
// Return one block to TLS.
607
0
inline void release_tls_block(IOBuf::Block* b) {
608
0
    if (!b) {
609
0
        return;
610
0
    }
611
0
    TLSData *tls_data = get_g_tls_data();
612
0
    if (b->full()) {
613
0
        b->dec_ref();
614
0
    } else if (tls_data->num_blocks >= max_blocks_per_thread()) {
615
0
        b->dec_ref();
616
        // g_num_hit_tls_threshold.fetch_add(1, butil::memory_order_relaxed);
617
0
        inc_g_num_hit_tls_threshold();
618
0
    } else {
619
0
        b->u.portal_next = tls_data->block_head;
620
0
        tls_data->block_head = b;
621
0
        ++tls_data->num_blocks;
622
0
        if (!tls_data->registered) {
623
0
            tls_data->registered = true;
624
0
            butil::thread_atexit(remove_tls_block_chain);
625
0
        }
626
0
    }
627
0
}
628
629
9
inline IOBuf::Block* create_block(const size_t block_size) {
630
9
    if (block_size > 0xFFFFFFFFULL) {
631
0
        LOG(FATAL) << "block_size=" << block_size << " is too large";
632
0
        return NULL;
633
0
    }
634
9
    char* mem = (char*)iobuf::blockmem_allocate(block_size);
635
9
    if (mem == NULL) {
636
0
        return NULL;
637
0
    }
638
9
    return new (mem) IOBuf::Block(mem + sizeof(IOBuf::Block),
639
9
                                  block_size - sizeof(IOBuf::Block));
640
9
}
641
642
9
inline IOBuf::Block* create_block() {
643
9
    return create_block(IOBuf::DEFAULT_BLOCK_SIZE);
644
9
}
645
646
void* cp(void *__restrict dest, const void *__restrict src, size_t n);
647
648
};  // namespace iobuf;
649
650
}  // namespace butil
651
652
#endif  // BUTIL_IOBUF_INL_H