• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

libbitcoin / libbitcoin-system / 9949774965

16 Jul 2024 02:30AM UTC coverage: 83.203% (+0.3%) from 82.874%
9949774965

Pull #1498

github

web-flow
Merge a266a0344 into 155e5fae6
Pull Request #1498: Optimizing deserializations.

205 of 222 new or added lines in 12 files covered. (92.34%)

14 existing lines in 8 files now uncovered.

10090 of 12127 relevant lines covered (83.2%)

4761709.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.4
/include/bitcoin/system/impl/stream/streamers/byte_reader.ipp
1
/**
2
 * Copyright (c) 2011-2023 libbitcoin developers (see AUTHORS)
3
 *
4
 * This file is part of libbitcoin.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
#ifndef LIBBITCOIN_SYSTEM_STREAM_STREAMERS_BYTE_READER_IPP
20
#define LIBBITCOIN_SYSTEM_STREAM_STREAMERS_BYTE_READER_IPP
21

22
#include <algorithm>
23
#include <ios>
24
#include <istream>
25
#include <limits>
26
#include <memory>
27
#include <string>
28
#include <bitcoin/system/data/data.hpp>
29
#include <bitcoin/system/define.hpp>
30
#include <bitcoin/system/endian/endian.hpp>
31
#include <bitcoin/system/error/error.hpp>
32
#include <bitcoin/system/math/math.hpp>
33
#include <bitcoin/system/stream/streamers/byte_writer.hpp>
34

35
namespace libbitcoin {
36
namespace system {
37
    
38
// Suppress istream members and allocator may throw inside NOEXCEPT.
39
// The intended behavior in this case is program abort.
40
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
41

42
// private/static
43
template <typename IStream>
44
inline typename byte_reader<IStream>::memory_arena
45
byte_reader<IStream>::default_arena() NOEXCEPT
13,084✔
46
{
47
    return bc::default_arena::get();
13,210✔
48
}
49

50
// All public methods must rely on protected for stream state except validity.
51

52
// constructors
53
// ----------------------------------------------------------------------------
54

55
template <typename IStream>
56
byte_reader<IStream>::byte_reader(IStream& source,
25,945✔
57
    const memory_arena& arena) NOEXCEPT
58
  : stream_(source),
13,214✔
59
    remaining_(system::maximum<size_t>),
13,214✔
60
    allocator_(arena)
13,214✔
61
{
62
    ////BC_ASSERT_MSG(stream_.exceptions() == IStream::goodbit,
63
    ////    "Input stream must not be configured to throw exceptions.");
64
}
25,458✔
65

66
// big endian
67
// ----------------------------------------------------------------------------
68

69
template <typename IStream>
70
template <typename Integer, size_t Size,
71
    if_integer<Integer>, if_not_greater<Size, sizeof(Integer)>>
72
Integer byte_reader<IStream>::read_big_endian() NOEXCEPT
634✔
73
{
74
    Integer value{};
634✔
75
    auto& bytes = byte_cast(value);
76

77
    // Call into virtual reader (vs. stream) so derived class can reuse.
78
    read_bytes(std::next(bytes.data(), sizeof(Integer) - Size), Size);
634✔
79
    return native_from_big_end(value);
634✔
80
}
81

82
template <typename IStream>
83
uint16_t byte_reader<IStream>::read_2_bytes_big_endian() NOEXCEPT
8✔
84
{
85
    return read_big_endian<uint16_t>();
8✔
86
}
87

88
template <typename IStream>
89
uint32_t byte_reader<IStream>::read_3_bytes_big_endian() NOEXCEPT
2✔
90
{
91
    return read_big_endian<uint32_t, 3>();
2✔
92
}
93

94
template <typename IStream>
95
uint32_t byte_reader<IStream>::read_4_bytes_big_endian() NOEXCEPT
600✔
96
{
97
    return read_big_endian<uint32_t>();
463✔
98
}
99

100
template <typename IStream>
101
uint64_t byte_reader<IStream>::read_5_bytes_big_endian() NOEXCEPT
2✔
102
{
103
    return read_big_endian<uint64_t, 5>();
2✔
104
}
105

106
template <typename IStream>
107
uint64_t byte_reader<IStream>::read_6_bytes_big_endian() NOEXCEPT
2✔
108
{
109
    return read_big_endian<uint64_t, 6>();
2✔
110
}
111

112
template <typename IStream>
113
uint64_t byte_reader<IStream>::read_7_bytes_big_endian() NOEXCEPT
2✔
114
{
115
    return read_big_endian<uint64_t, 7>();
2✔
116
}
117

118
template <typename IStream>
119
uint64_t byte_reader<IStream>::read_8_bytes_big_endian() NOEXCEPT
10✔
120
{
121
    return read_big_endian<uint64_t>();
10✔
122
}
123

124
// little endian
125
// ----------------------------------------------------------------------------
126

127
template <typename IStream>
128
template <typename Integer, size_t Size,
129
    if_integer<Integer>, if_not_greater<Size, sizeof(Integer)>>
130
Integer byte_reader<IStream>::read_little_endian() NOEXCEPT
21,318✔
131
{
132
    Integer value{};
21,318✔
133
    auto& bytes = byte_cast(value);
134

135
    // Call into virtual reader (vs. stream) so derived class can reuse.
136
    read_bytes(bytes.data(), Size);
21,318✔
137
    return native_from_little_end(value);
21,318✔
138
}
139

140
template <typename IStream>
141
uint16_t byte_reader<IStream>::read_2_bytes_little_endian() NOEXCEPT
39✔
142
{
143
    return read_little_endian<uint16_t>();
39✔
144
}
145

146
template <typename IStream>
147
uint32_t byte_reader<IStream>::read_3_bytes_little_endian() NOEXCEPT
2✔
148
{
149
    return read_little_endian<uint32_t, 3>();
2✔
150
}
151

152
template <typename IStream>
153
uint32_t byte_reader<IStream>::read_4_bytes_little_endian() NOEXCEPT
1,248✔
154
{
155
    return read_little_endian<uint32_t>();
1,248✔
156
}
157

158
template <typename IStream>
159
uint64_t byte_reader<IStream>::read_5_bytes_little_endian() NOEXCEPT
2✔
160
{
161
    return read_little_endian<uint64_t, 5>();
2✔
162
}
163

164
template <typename IStream>
165
uint64_t byte_reader<IStream>::read_6_bytes_little_endian() NOEXCEPT
2✔
166
{
167
    return read_little_endian<uint64_t, 6>();
2✔
168
}
169

170
template <typename IStream>
171
uint64_t byte_reader<IStream>::read_7_bytes_little_endian() NOEXCEPT
2✔
172
{
173
    return read_little_endian<uint64_t, 7>();
2✔
174
}
175

176
template <typename IStream>
177
uint64_t byte_reader<IStream>::read_8_bytes_little_endian() NOEXCEPT
20,007✔
178
{
179
    return read_little_endian<uint64_t>();
20,007✔
180
}
181

182
template <typename IStream>
183
uint64_t byte_reader<IStream>::read_variable() NOEXCEPT
1,091✔
184
{
185
    switch (const auto value = read_byte())
1,091✔
186
    {
187
        case varint_eight_bytes:
12✔
188
            return read_8_bytes_little_endian();
12✔
189
        case varint_four_bytes:
12✔
190
            return read_4_bytes_little_endian();
12✔
191
        case varint_two_bytes:
22✔
192
            return read_2_bytes_little_endian();
22✔
193
        default:
1,045✔
194
            return value;
1,045✔
195
    }
196
}
197

198
template <typename IStream>
199
size_t byte_reader<IStream>::read_size(size_t limit) NOEXCEPT
1,071✔
200
{
201
    const auto size = read_variable();
1,071✔
202

203
    // This facilitates safely passing the size into a follow-on reader.
204
    // Return zero allows follow-on use before testing reader state.
205
    if (size > limit)
1,071✔
206
    {
207
        invalid();
×
208
        return zero;
×
209
    }
210
 
211
    return possible_narrow_cast<size_t>(size);
212
}
213

214
template <typename IStream>
215
code byte_reader<IStream>::read_error_code() NOEXCEPT
8✔
216
{
217
    const auto value = read_little_endian<uint32_t>();
8✔
218
    return code(static_cast<error::error_t>(value));
8✔
219
}
220

221
template <typename IStream>
222
uint8_t byte_reader<IStream>::peek_byte() NOEXCEPT
54✔
223
{
224
    return do_peek_byte();
54✔
225
}
226

227
template <typename IStream>
228
uint8_t byte_reader<IStream>::read_byte() NOEXCEPT
201,152✔
229
{
230
    uint8_t value = pad();
201,152✔
231
    do_read_bytes(&value, one);
201,152✔
232
    return value;
201,152✔
233
}
234

235
// byte arrays
236
// ----------------------------------------------------------------------------
237

238
template <typename IStream>
239
template <size_t Size>
240
data_array<Size> byte_reader<IStream>::read_forward() NOEXCEPT
318✔
241
{
242
    // Truncated bytes are populated with 0x00.
243
    // Reader supports directly populating an array, this avoids a copy.
244
    data_array<Size> out{};
318✔
245
    do_read_bytes(out.data(), Size);
318✔
246
    return out;
318✔
247
}
248

249
template <typename IStream>
250
template <size_t Size>
251
data_array<Size> byte_reader<IStream>::read_reverse() NOEXCEPT
12✔
252
{
253
    return system::reverse(read_forward<Size>());
12✔
254
}
255

256
template <typename IStream>
257
template <size_t Size>
258
data_array_cptr<Size> byte_reader<IStream>::read_forward_cptr() NOEXCEPT
11✔
259
{
260
    if (!valid())
11✔
261
        return {};
262

263
    const auto cptr = to_allocated<data_array<Size>>(get_arena());
11✔
264
    if (!cptr)
11✔
265
    {
266
        invalidate();
×
267
        return cptr;
268
    }
269

270
    // Truncated bytes are populated with 0x00.
271
    // Reader supports directly populating an array, this avoids a copy.
272
    BC_PUSH_WARNING(NO_UNGUARDED_POINTERS)
273
    do_read_bytes(to_non_const_raw_ptr(cptr)->data(), Size);
11✔
274
    BC_POP_WARNING()
275

276
    if (!valid())
11✔
277
        return {};
278

279
    return cptr;
280
}
281

282
template <typename IStream>
283
template <size_t Size>
284
data_array_cptr<Size> byte_reader<IStream>::read_reverse_cptr() NOEXCEPT
3✔
285
{
286
    const auto cptr = read_forward_cptr<Size>();
3✔
287
    if (!cptr)
3✔
288
        return cptr;
289

290
    // Guarded above.
291
    BC_PUSH_WARNING(NO_UNGUARDED_POINTERS)
292
    const auto ptr = to_non_const_raw_ptr(cptr);
293
    BC_POP_WARNING()
294

295
    std::reverse(ptr->begin(), ptr->end());
1✔
296
    return cptr;
1✔
297
}
298

299
template <typename IStream>
300
mini_hash byte_reader<IStream>::read_mini_hash() NOEXCEPT
8✔
301
{
302
    return read_forward<mini_hash_size>();
8✔
303
}
304

305
template <typename IStream>
306
short_hash byte_reader<IStream>::read_short_hash() NOEXCEPT
4✔
307
{
308
    return read_forward<short_hash_size>();
4✔
309
}
310

311
template <typename IStream>
312
hash_digest byte_reader<IStream>::read_hash() NOEXCEPT
248✔
313
{
314
    return read_forward<hash_size>();
248✔
315
}
316

317
template <typename IStream>
318
long_hash byte_reader<IStream>::read_long_hash() NOEXCEPT
4✔
319
{
320
    return read_forward<long_hash_size>();
4✔
321
}
322

323
template <typename IStream>
324
mini_hash_cptr byte_reader<IStream>::read_mini_hash_cptr() NOEXCEPT
2✔
325
{
326
    return read_forward_cptr<mini_hash_size>();
2✔
327
}
328

329
template <typename IStream>
330
short_hash_cptr byte_reader<IStream>::read_short_hash_cptr() NOEXCEPT
1✔
331
{
332
    return read_forward_cptr<short_hash_size>();
1✔
333
}
334

335
template <typename IStream>
336
hash_cptr byte_reader<IStream>::read_hash_cptr() NOEXCEPT
1✔
337
{
338
    return read_forward_cptr<hash_size>();
1✔
339
}
340

341
template <typename IStream>
342
long_hash_cptr byte_reader<IStream>::read_long_hash_cptr() NOEXCEPT
1✔
343
{
344
    return read_forward_cptr<long_hash_size>();
1✔
345
}
346

347
// byte vectors
348
// ----------------------------------------------------------------------------
349

350
template <typename IStream>
351
data_chunk byte_reader<IStream>::read_bytes() NOEXCEPT
40✔
352
{
353
    // Count bytes to the end, avoids push_back reallocations.
354
    size_t size{};
355
    while (!get_exhausted())
417✔
356
    {
357
        ++size;
377✔
358
        skip_byte();
377✔
359
    };
360

361
    rewind_bytes(size);
40✔
362
    return read_bytes(size);
40✔
363
}
364

365
template <typename IStream>
366
chunk_cptr byte_reader<IStream>::read_bytes_cptr() NOEXCEPT
4✔
367
{
368
    // Count bytes to the end, avoids push_back reallocations.
369
    size_t size{};
370
    while (!get_exhausted())
7✔
371
    {
372
        ++size;
3✔
373
        skip_byte();
3✔
374
    };
375

376
    rewind_bytes(size);
4✔
377
    return read_bytes_cptr(size);
4✔
378
}
379

380
template <typename IStream>
381
data_chunk byte_reader<IStream>::read_bytes(size_t size) NOEXCEPT
266✔
382
{
383
    if (is_zero(size))
257✔
384
        return {};
30✔
385

386
    // This allows caller to read an invalid stream without allocation.
387
    if (!valid())
236✔
388
        return {};
×
389

390
    data_chunk out(size);
236✔
391
    do_read_bytes(out.data(), size);
236✔
392
    return out;
393
}
394

395
template <typename IStream>
396
chunk_cptr byte_reader<IStream>::read_bytes_cptr(size_t size) NOEXCEPT
10✔
397
{
398
    // This allows caller to read an invalid stream without allocation.
399
    if (!valid())
10✔
400
        return {};
401

402
    // TODO: bypass vector byte fill.
403
    const auto cptr = to_allocated<data_chunk>(get_arena(), size);
8✔
404
    if (!cptr)
8✔
405
    {
406
        invalidate();
×
407
        return cptr;
408
    }
409

410
    if (is_zero(size))
8✔
411
        return cptr;
412

413
    // Guarded above.
414
    BC_PUSH_WARNING(NO_UNGUARDED_POINTERS)
415
    do_read_bytes(to_non_const_raw_ptr(cptr)->data(), size);
4✔
416
    BC_POP_WARNING()
417

418
    if (!valid())
4✔
419
        return {};
420

421
    return cptr;
422
}
423

424
template <typename IStream>
425
data_chunk* byte_reader<IStream>::read_bytes_raw(size_t size) NOEXCEPT
82,657✔
426
{
427
    // This allows caller to read an invalid stream without allocation.
428
    if (!valid())
82,657✔
429
        return nullptr;
430

431
    // TODO: bypass vector byte fill.
432
    const auto raw = allocator_.new_object<data_chunk>(size);
82,652✔
433
    if (raw == nullptr)
82,652✔
434
    {
NEW
435
        invalidate();
×
NEW
436
        return raw;
×
437
    }
438

439
    if (is_zero(size))
82,652✔
440
        return raw;
441

442
    do_read_bytes(raw->data(), size);
955✔
443
    if (!valid())
955✔
444
    {
445
        allocator_.delete_object<data_chunk>(raw);
8✔
446
        return nullptr;
8✔
447
    }
448

449
    return raw;
450
}
451

452
template <typename IStream>
453
void byte_reader<IStream>::read_bytes(uint8_t* buffer, size_t size) NOEXCEPT
22,406✔
454
{
455
    do_read_bytes(buffer, size);
22,406✔
456
}
22,406✔
457

458
// strings
459
// ----------------------------------------------------------------------------
460

461
template <typename IStream>
462
std::string byte_reader<IStream>::read_string(size_t limit) NOEXCEPT
16✔
463
{
464
    return read_string_buffer(read_size(limit));
16✔
465
}
466

467
template <typename IStream>
468
std::string byte_reader<IStream>::read_string_buffer(size_t size) NOEXCEPT
40✔
469
{
470
    // Isolating get_exhausted to first call is an optimization (must clear).
471
    if (get_exhausted())
40✔
472
        return {};
473

474
    // This will produce one (zero) terminating byte if size exceeds available.
475
    std::string out{};
476
    out.reserve(add1(size));
36✔
477
    while (!is_zero(size--) && valid())
236✔
478
        out.push_back(read_byte());
200✔
479

480
    // Removes zero and all after, required for bitcoin string deserialization.
481
    const auto position = out.find('\0');
36✔
482
    out.resize(position == std::string::npos ? out.size() : position);
68✔
483
    ////out.shrink_to_fit();
484

485
    clear();
36✔
486
    return out;
36✔
487
}
488

489
// streams
490
// ----------------------------------------------------------------------------
491

492
template <typename IStream>
493
std::ostream& byte_reader<IStream>::read(std::ostream& out) NOEXCEPT
8✔
494
{
495
    // This creates an intermediate buffer the size of the stream.
496
    // This is presumed to be more optimal than looping individual bytes.
497
    byte_writer<std::ostream>(out).write_bytes(read_bytes());
8✔
498
    return out;
8✔
499
}
500

501
// context
502
// ----------------------------------------------------------------------------
503

504
template <typename IStream>
505
void byte_reader<IStream>::skip_byte() NOEXCEPT
561✔
506
{
507
    do_skip_bytes(one);
561✔
508
}
561✔
509

510
template <typename IStream>
511
void byte_reader<IStream>::skip_bytes(size_t size) NOEXCEPT
82,611✔
512
{
513
    do_skip_bytes(size);
82,611✔
514
}
82,611✔
515

516
template <typename IStream>
517
void byte_reader<IStream>::skip_variable() NOEXCEPT
24✔
518
{
519
    switch (read_byte())
24✔
520
    {
521
        case varint_eight_bytes:
8✔
522
            do_skip_bytes(8);
8✔
523
            return;
8✔
524
        case varint_four_bytes:
4✔
525
            do_skip_bytes(4);
4✔
526
            return;
4✔
527
        case varint_two_bytes:
4✔
528
            do_skip_bytes(2);
4✔
529
            return;
4✔
530
        default:
531
            return;
532
    }
533
}
534

535
template <typename IStream>
536
void byte_reader<IStream>::rewind_byte() NOEXCEPT
4✔
537
{
538
    do_rewind_bytes(one);
4✔
539
}
4✔
540

541
template <typename IStream>
542
void byte_reader<IStream>::rewind_bytes(size_t size) NOEXCEPT
68✔
543
{
544
    do_rewind_bytes(size);
68✔
545
}
68✔
546

547
template <typename IStream>
548
void byte_reader<IStream>::set_position(size_t absolute) NOEXCEPT
671✔
549
{
550
    // Clear a presumed error state following a read overflow.
551
    clear();
671✔
552

553
    // This allows conversion of and absolute to relative position.
554
    const auto position = get_read_position();
671✔
555

556
    if (absolute == position)
671✔
557
        return;
558

559
    if (absolute > position)
627✔
560
        do_skip_bytes(absolute - position);
6✔
561
    else
562
        do_rewind_bytes(position - absolute);
621✔
563
}
564

565
template <typename IStream>
566
bool byte_reader<IStream>::is_exhausted() const NOEXCEPT
371,908✔
567
{
568
    // True if invalid or if no bytes remain in the stream.
569
    return get_exhausted();
371,908✔
570
}
571

572
// control
573
// ----------------------------------------------------------------------------
574
// These only call non-virtual (private) methods.
575

576
template <typename IStream>
577
size_t byte_reader<IStream>::get_read_position() NOEXCEPT
85,234✔
578
{
579
    return getter();
85,234✔
580
}
581

582
template <typename IStream>
583
void byte_reader<IStream>::set_limit(size_t size) NOEXCEPT
1,036✔
584
{
585
    limit(size);
586
}
1,034✔
587

588
template <typename IStream>
589
void byte_reader<IStream>::invalidate() NOEXCEPT
34✔
590
{
591
    // Permanently invalidate the stream/reader.
592
    invalid();
34✔
593
}
17✔
594

595
template <typename IStream>
596
byte_reader<IStream>::operator bool() const NOEXCEPT
1,783✔
597
{
598
    // True if any call created an error state, even if there have been
599
    // subsequent calls, or if any error state preexists on the stream.
600
    return valid();
1,783✔
601
}
602

603
// This should not be necessary with bool() defined, but it is.
604
template <typename IStream>
605
bool byte_reader<IStream>::operator!() const NOEXCEPT
371,343✔
606
{
607
    return !valid();
371,343✔
608
}
609

610
template <typename IStream>
611
typename byte_reader<IStream>::memory_arena
612
byte_reader<IStream>::get_arena() const NOEXCEPT
85,526✔
613
{
614
    return allocator_.resource();
85,522✔
615
}
616

617
template <typename IStream>
618
typename byte_reader<IStream>::memory_allocator&
619
byte_reader<IStream>::get_allocator() const NOEXCEPT
85,993✔
620
{
621
    return allocator_;
85,993✔
622
}
623

624
// protected virtual
625
// ----------------------------------------------------------------------------
626
// These may only call non-virtual (private) methods (due to overriding).
627

628
template <typename IStream>
629
uint8_t byte_reader<IStream>::do_peek_byte() NOEXCEPT
54✔
630
{
631
    if (limiter(one))
54✔
632
        return pad();
633

634
    // This sets eofbit (or badbit) on empty and eofbit if otherwise at end.
635
    // eofbit does not cause !!eofbit == true, but badbit does, so we validate
636
    // the call the achieve consistent behavior. The reader will be invalid if
637
    // the stream is peeked past end, including when empty.
638
    const auto value =
639
        possible_narrow_and_sign_cast<uint8_t>(stream_.peek());
52✔
640

641
    validate();
52✔
642
    return valid() ? value : pad();
52✔
643
}
644

645
template <typename IStream>
646
void byte_reader<IStream>::do_read_bytes(uint8_t* buffer, size_t size) NOEXCEPT
491,798✔
647
{
648
    // Limited reads are not partially filled or padded.
649
    if (limiter(size))
491,798✔
650
        return;
651

652
    // It is not generally more efficient to call stream_.get() for one byte.
653
    // Partially-failed reads here will be populated by the stream, not padded.
654
    // However, both copy_source and stringstream will zero-fill partial reads.
655
    // Read on empty is inconsistent, so validate the result. The reader will be
656
    // invalid if the stream is get past end, including when empty.
657

658
    // Read past stream end invalidates stream unless size exceeds maximum.
659
    BC_ASSERT(size <= maximum);
660
    stream_.read(pointer_cast<char>(buffer),
491,792✔
661
        possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
662

663
    validate();
491,792✔
664
}
665

666
template <typename IStream>
667
void byte_reader<IStream>::do_skip_bytes(size_t size) NOEXCEPT
83,090✔
668
{
669
    if (limiter(size))
83,090✔
670
        return;
671

672
    // Skip past stream end invalidates stream unless size exceeds maximum.
673
    BC_ASSERT(size <= maximum);
674
    seeker(possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
83,084✔
675
}
676

677
template <typename IStream>
678
void byte_reader<IStream>::do_rewind_bytes(size_t size) NOEXCEPT
876✔
679
{
680
    // Given that the stream size is unknown to the reader, the limit may be
681
    // arbitrarily high. This prevents an overflow if sum exceeds max_size_t.
682
    // max_size_t is presumed to exceed the range of IStream::pos_type, in 
683
    // which case this constraint does not affect the limiting behavior.
684
    remaining_ = ceilinged_add(remaining_, size);
1,748✔
685

686
    // Rewind past stream start invalidates stream unless size exceeds maximum.
687
    BC_ASSERT(size <= maximum);
688
    seeker(-possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
876✔
689
}
876✔
690

691
template <typename IStream>
692
bool byte_reader<IStream>::get_exhausted() const NOEXCEPT
195,854✔
693
{
694
    if (is_zero(remaining_))
195,854✔
695
        return true;
696

697
    // Empty behavior is broadly inconsistent across implementations.
698
    // It is also necessary to start many reads, including initial reads, with
699
    // an exhaustion check, which must be consistent and not state-changing.
700
    // A state change would preclude testing for errors after testing for end.
701
    // This method is const because it reliably creates no net state change.
702
    // Streams are valid unless read or peeked. Peek does not change stream
703
    // position, so it is used to force the stream into a failure state when
704
    // empty. peek past end always sets eofbit but peek state on empty is
705
    // inconsistent across streams, though a flag is always set. eofbit is set
706
    // by istringstream and badbit is set by boost.
707

708
    if (!valid())
194,820✔
709
        return true;
710

711
    // Peek to force error on eof, save condition, restore valid stream state.
712
    stream_.peek();
194,804✔
713
    const auto eof = !valid();
194,804✔
714
    stream_.clear();
194,804✔
715

716
    return eof;
194,804✔
717
}
718

719
// private
720
// ----------------------------------------------------------------------------
721
// These may only call other private methods (due to overriding).
722

723
template <typename IStream>
724
bool byte_reader<IStream>::valid() const NOEXCEPT
1,426,230✔
725
{
726
    // zero is the istream documented flag for no error.
727
    return stream_.rdstate() == IStream::goodbit;
1,426,230✔
728
}
729

730
template <typename IStream>
731
void byte_reader<IStream>::invalid() NOEXCEPT
12,292✔
732
{
733
    // If eofbit is set, failbit is generally set on all operations.
734
    // badbit is unrecoverable, set the others to ensure consistency.
735
    stream_.setstate(IStream::eofbit | IStream::failbit | IStream::badbit);
12,292✔
736
}
12,292✔
737

738
template <typename IStream>
739
void byte_reader<IStream>::validate() NOEXCEPT
579,344✔
740
{
741
    // Ensure that any failure in the call fully invalidates the stream/reader.
742
    // Some errors are recoverable, so a sequence of operations without testing
743
    // for validity could miss an error on intervening operations. For example,
744
    // seekg can reset eofbit and read past doesn't set badbit (recoverable).
745
    if (!valid())
579,344✔
746
        invalid();
12,258✔
747
}
579,344✔
748

749
template <typename IStream>
750
void byte_reader<IStream>::clear() NOEXCEPT
707✔
751
{
752
    // Does not reset the current position.
753
    stream_.clear();
707✔
754
}
707✔
755

756
template <typename IStream>
757
size_t byte_reader<IStream>::getter() NOEXCEPT
85,234✔
758
{
759
    static const auto failure = typename IStream::pos_type(-1);
85,234✔
760
    typename IStream::pos_type position;
761

762
    // Force these to be consistent, and avoid propagating exceptions.
763
    // Assuming behavior is consistent with seekg (as documented).
764
    // Returns current position on success and pos_type(-1) on failure.
765
    try
766
    {
767
        // This does not honor BOOST_EXCEPTION_DISABLE.
768
        position = stream_.tellg();
85,234✔
769
        validate();
85,234✔
770
    }
771
    catch (const typename IStream::failure&)
×
772
    {
773
        position = failure;
×
774
        invalid();
×
775
    }
776

777
    // Max size_t is presumed to exceed max IStream::pos_type.
778
    return position == failure ? zero :
85,234✔
779
        possible_narrow_and_sign_cast<size_t>(position);
85,234✔
780
}
781

782
template <typename IStream>
783
void byte_reader<IStream>::limit(size_t size) NOEXCEPT
1,036✔
784
{
785
    remaining_ = size;
1,036✔
786
}
787

788
template <typename IStream>
789
bool byte_reader<IStream>::limiter(size_t size) NOEXCEPT
574,942✔
790
{
791
    if (size > remaining_)
574,942✔
792
    {
793
        // Does not reset the current position or the remaining limit.
794
        invalidate();
14✔
795
        return true;
14✔
796
    }
797

798
    remaining_ -= size;
574,928✔
799
    return false;
574,928✔
800
}
801

802
template <typename IStream>
803
void byte_reader<IStream>::seeker(typename IStream::pos_type offset) NOEXCEPT
83,960✔
804
{
805
    // Force these to be consistent by treating zero seek as a no-op.
806
    // boost/istringstream both succeed on non-empty zero seek.
807
    // istringstream succeeds on empty zero seek, boost sets failbit.
808
    if (is_zero(offset))
83,960✔
809
        return;
810

811
    // Force these to be consistent, and avoid propagating exceptions.
812
    // istringstream sets failbit on non-empty over/underflow, boost throws.
813
    // boost/istringstream both set failbit on empty over/underflow.
814
    try
815
    {
816
        // This does not honor BOOST_EXCEPTION_DISABLE.
817
        stream_.seekg(offset, IStream::cur);
2,266✔
818
        validate();
2,266✔
819
    }
820
    catch (const typename IStream::failure&)
×
821
    {
822
        invalid();
×
823
    }
824
}
825

826
BC_POP_WARNING()
827

828
} // namespace system
829
} // namespace libbitcoin
830

831
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc