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

libbitcoin / libbitcoin-system / 9847462002

08 Jul 2024 09:55PM UTC coverage: 82.783% (+0.3%) from 82.53%
9847462002

push

github

web-flow
Merge pull request #1495 from evoskuil/master

Merging with macOS13 break, now that we are aware that the cause is the platform.

15 of 17 new or added lines in 3 files covered. (88.24%)

2 existing lines in 1 file now uncovered.

9905 of 11965 relevant lines covered (82.78%)

4766706.76 hits per line

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

95.56
/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 <memory_resource>
28
#include <string>
29
#include <bitcoin/system/data/data.hpp>
30
#include <bitcoin/system/define.hpp>
31
#include <bitcoin/system/endian/endian.hpp>
32
#include <bitcoin/system/error/error.hpp>
33
#include <bitcoin/system/math/math.hpp>
34
#include <bitcoin/system/stream/streamers/byte_writer.hpp>
35

36
namespace libbitcoin {
37
namespace system {
38

39
// private/static
40
template <typename IStream>
41
inline typename byte_reader<IStream>::memory_allocator
42
byte_reader<IStream>::default_allocator() NOEXCEPT
13,204✔
43
{
44
    return std::pmr::get_default_resource();
13,204✔
45
}
46

47
// All public methods must rely on protected for stream state except validity.
48

49
// constructors
50
// ----------------------------------------------------------------------------
51

52
template <typename IStream>
53
byte_reader<IStream>::byte_reader(IStream& source,
25,935✔
54
    const memory_allocator& allocator) NOEXCEPT
55
  : stream_(source),
13,206✔
56
    remaining_(system::maximum<size_t>),
13,206✔
57
    allocator_(allocator)
13,206✔
58
{
59
    ////BC_ASSERT_MSG(stream_.exceptions() == IStream::goodbit,
60
    ////    "Input stream must not be configured to throw exceptions.");
61
}
25,458✔
62

63
// big endian
64
// ----------------------------------------------------------------------------
65

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

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

79
template <typename IStream>
80
uint16_t byte_reader<IStream>::read_2_bytes_big_endian() NOEXCEPT
8✔
81
{
82
    return read_big_endian<uint16_t>();
8✔
83
}
84

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

91
template <typename IStream>
92
uint32_t byte_reader<IStream>::read_4_bytes_big_endian() NOEXCEPT
600✔
93
{
94
    return read_big_endian<uint32_t>();
463✔
95
}
96

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

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

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

115
template <typename IStream>
116
uint64_t byte_reader<IStream>::read_8_bytes_big_endian() NOEXCEPT
10✔
117
{
118
    return read_big_endian<uint64_t>();
10✔
119
}
120

121
// little endian
122
// ----------------------------------------------------------------------------
123

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

132
    // Call into virtual reader (vs. stream) so derived class can reuse.
133
    read_bytes(bytes.data(), Size);
21,264✔
134
    return native_from_little_end(value);
21,264✔
135
}
136

137
template <typename IStream>
138
uint16_t byte_reader<IStream>::read_2_bytes_little_endian() NOEXCEPT
39✔
139
{
140
    return read_little_endian<uint16_t>();
39✔
141
}
142

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

149
template <typename IStream>
150
uint32_t byte_reader<IStream>::read_4_bytes_little_endian() NOEXCEPT
1,202✔
151
{
152
    return read_little_endian<uint32_t>();
1,202✔
153
}
154

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

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

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

173
template <typename IStream>
174
uint64_t byte_reader<IStream>::read_8_bytes_little_endian() NOEXCEPT
19,999✔
175
{
176
    return read_little_endian<uint64_t>();
19,999✔
177
}
178

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

195
template <typename IStream>
196
size_t byte_reader<IStream>::read_size(size_t limit) NOEXCEPT
1,020✔
197
{
198
    const auto size = read_variable();
1,020✔
199

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

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

218
template <typename IStream>
219
uint8_t byte_reader<IStream>::peek_byte() NOEXCEPT
50✔
220
{
221
    return do_peek_byte();
50✔
222
}
223

224
template <typename IStream>
225
uint8_t byte_reader<IStream>::read_byte() NOEXCEPT
201,057✔
226
{
227
    uint8_t value = pad();
201,057✔
228
    do_read_bytes(&value, one);
201,057✔
229
    return value;
201,057✔
230
}
231

232
// byte arrays
233
// ----------------------------------------------------------------------------
234

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

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

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

260
    const auto cptr = to_allocated<data_array<Size>>(allocator_);
11✔
261
    if (!cptr)
11✔
262
    {
UNCOV
263
        invalidate();
×
264
        return cptr;
265
    }
266

267
    // Guarded above.
268
    BC_PUSH_WARNING(NO_UNGUARDED_POINTERS)
269
    const auto ptr = to_non_const_raw_ptr(cptr);
270
    BC_POP_WARNING()
271

272
    // Truncated bytes are populated with 0x00.
273
    // Reader supports directly populating an array, this avoids a copy.
274
    do_read_bytes(ptr->data(), Size);
11✔
275
    if (!valid())
11✔
276
        return {};
277

278
    return cptr;
279
}
280

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

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

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

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

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

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

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

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

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

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

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

346
// byte vectors
347
// ----------------------------------------------------------------------------
348

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

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

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

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

379
template <typename IStream>
380
data_chunk byte_reader<IStream>::read_bytes(size_t size) NOEXCEPT
82,883✔
381
{
382
    if (is_zero(size))
82,874✔
383
        return {};
81,697✔
384

385
    // This allows caller to read an invalid stream without allocation.
386
    if (!valid())
1,186✔
NEW
387
        return {};
×
388

389
    data_chunk out(size);
1,186✔
390
    do_read_bytes(out.data(), size);
1,186✔
391
    return out;
392
}
393

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

401
    const auto cptr = to_allocated<data_chunk>(allocator_, size);
8✔
402
    if (!cptr)
8✔
403
    {
UNCOV
404
        invalidate();
×
405
        return cptr;
406
    }
407

408
    if (is_zero(size))
8✔
409
        return cptr;
410

411
    BC_PUSH_WARNING(NO_UNGUARDED_POINTERS)
412
    const auto ptr = const_cast<data_chunk*>(cptr.get());
413
    BC_POP_WARNING()
414

415
    do_read_bytes(ptr->data(), size);
4✔
416
    if (!valid())
4✔
417
        return {};
418

419
    return cptr;
420
}
421

422
template <typename IStream>
423
void byte_reader<IStream>::read_bytes(uint8_t* buffer, size_t size) NOEXCEPT
21,918✔
424
{
425
    do_read_bytes(buffer, size);
21,918✔
426
}
21,918✔
427

428
// strings
429
// ----------------------------------------------------------------------------
430

431
template <typename IStream>
432
std::string byte_reader<IStream>::read_string(size_t limit) NOEXCEPT
16✔
433
{
434
    return read_string_buffer(read_size(limit));
16✔
435
}
436

437
template <typename IStream>
438
std::string byte_reader<IStream>::read_string_buffer(size_t size) NOEXCEPT
40✔
439
{
440
    // Isolating get_exhausted to first call is an optimization (must clear).
441
    if (get_exhausted())
40✔
442
        return {};
443

444
    // This will produce one (zero) terminating byte if size exceeds available.
445
    std::string out{};
446
    out.reserve(add1(size));
36✔
447
    while (!is_zero(size--) && valid())
236✔
448
        out.push_back(read_byte());
200✔
449

450
    // Removes zero and all after, required for bitcoin string deserialization.
451
    const auto position = out.find('\0');
36✔
452
    out.resize(position == std::string::npos ? out.size() : position);
68✔
453
    ////out.shrink_to_fit();
454

455
    clear();
36✔
456
    return out;
36✔
457
}
458

459
// streams
460
// ----------------------------------------------------------------------------
461

462
template <typename IStream>
463
std::ostream& byte_reader<IStream>::read(std::ostream& out) NOEXCEPT
8✔
464
{
465
    // This creates an intermediate buffer the size of the stream.
466
    // This is presumed to be more optimal than looping individual bytes.
467
    byte_writer<std::ostream>(out).write_bytes(read_bytes());
8✔
468
    return out;
8✔
469
}
470

471
// context
472
// ----------------------------------------------------------------------------
473

474
template <typename IStream>
475
void byte_reader<IStream>::skip_byte() NOEXCEPT
555✔
476
{
477
    do_skip_bytes(one);
555✔
478
}
555✔
479

480
template <typename IStream>
481
void byte_reader<IStream>::skip_bytes(size_t size) NOEXCEPT
82,588✔
482
{
483
    do_skip_bytes(size);
82,588✔
484
}
82,588✔
485

486
template <typename IStream>
487
void byte_reader<IStream>::skip_variable() NOEXCEPT
24✔
488
{
489
    switch (read_byte())
24✔
490
    {
491
        case varint_eight_bytes:
8✔
492
            do_skip_bytes(8);
8✔
493
            return;
8✔
494
        case varint_four_bytes:
4✔
495
            do_skip_bytes(4);
4✔
496
            return;
4✔
497
        case varint_two_bytes:
4✔
498
            do_skip_bytes(2);
4✔
499
            return;
4✔
500
        default:
501
            return;
502
    }
503
}
504

505
template <typename IStream>
506
void byte_reader<IStream>::rewind_byte() NOEXCEPT
4✔
507
{
508
    do_rewind_bytes(one);
4✔
509
}
4✔
510

511
template <typename IStream>
512
void byte_reader<IStream>::rewind_bytes(size_t size) NOEXCEPT
68✔
513
{
514
    do_rewind_bytes(size);
68✔
515
}
68✔
516

517
template <typename IStream>
518
void byte_reader<IStream>::set_position(size_t absolute) NOEXCEPT
655✔
519
{
520
    // Clear a presumed error state following a read overflow.
521
    clear();
655✔
522

523
    // This allows conversion of and absolute to relative position.
524
    const auto position = get_read_position();
655✔
525

526
    if (absolute == position)
655✔
527
        return;
528

529
    if (absolute > position)
617✔
530
        do_skip_bytes(absolute - position);
6✔
531
    else
532
        do_rewind_bytes(position - absolute);
611✔
533
}
534

535
template <typename IStream>
536
bool byte_reader<IStream>::is_exhausted() const NOEXCEPT
371,832✔
537
{
538
    // True if invalid or if no bytes remain in the stream.
539
    return get_exhausted();
371,832✔
540
}
541

542
// control
543
// ----------------------------------------------------------------------------
544
// These only call non-virtual (private) methods.
545

546
template <typename IStream>
547
size_t byte_reader<IStream>::get_read_position() NOEXCEPT
85,148✔
548
{
549
    return getter();
85,148✔
550
}
551

552
template <typename IStream>
553
void byte_reader<IStream>::set_limit(size_t size) NOEXCEPT
1,004✔
554
{
555
    limit(size);
556
}
1,002✔
557

558
template <typename IStream>
559
void byte_reader<IStream>::invalidate() NOEXCEPT
33✔
560
{
561
    // Permanently invalidate the stream/reader.
562
    invalid();
33✔
563
}
17✔
564

565
template <typename IStream>
566
byte_reader<IStream>::operator bool() const NOEXCEPT
84,310✔
567
{
568
    // True if any call created an error state, even if there have been
569
    // subsequent calls, or if any error state preexists on the stream.
570
    return valid();
84,310✔
571
}
572

573
// This should not be necessary with bool() defined, but it is.
574
template <typename IStream>
575
bool byte_reader<IStream>::operator!() const NOEXCEPT
371,277✔
576
{
577
    return !valid();
371,277✔
578
}
579

580
template <typename IStream>
581
const typename byte_reader<IStream>::memory_allocator&
582
byte_reader<IStream>::allocator() const NOEXCEPT
2✔
583
{
NEW
584
    return allocator_;
×
585
}
586

587
// protected virtual
588
// ----------------------------------------------------------------------------
589
// These may only call non-virtual (private) methods (due to overriding).
590

591
// Suppress istream members may throw inside NOEXCEPT.
592
// The intended behavior in this case is program abort.
593
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
594

595
template <typename IStream>
596
uint8_t byte_reader<IStream>::do_peek_byte() NOEXCEPT
50✔
597
{
598
    if (limiter(one))
50✔
599
        return pad();
600

601
    // This sets eofbit (or badbit) on empty and eofbit if otherwise at end.
602
    // eofbit does not cause !!eofbit == true, but badbit does, so we validate
603
    // the call the achieve consistent behavior. The reader will be invalid if
604
    // the stream is peeked past end, including when empty.
605
    const auto value =
606
        possible_narrow_and_sign_cast<uint8_t>(stream_.peek());
48✔
607

608
    validate();
48✔
609
    return valid() ? value : pad();
48✔
610
}
611

612
template <typename IStream>
613
void byte_reader<IStream>::do_read_bytes(uint8_t* buffer, size_t size) NOEXCEPT
491,628✔
614
{
615
    // Limited reads are not partially filled or padded.
616
    if (limiter(size))
491,628✔
617
        return;
618

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

625
    // Read past stream end invalidates stream unless size exceeds maximum.
626
    BC_ASSERT(size <= maximum);
627
    stream_.read(pointer_cast<char>(buffer),
491,622✔
628
        possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
629

630
    validate();
491,622✔
631
}
632

633
template <typename IStream>
634
void byte_reader<IStream>::do_skip_bytes(size_t size) NOEXCEPT
83,061✔
635
{
636
    if (limiter(size))
83,061✔
637
        return;
638

639
    // Skip past stream end invalidates stream unless size exceeds maximum.
640
    BC_ASSERT(size <= maximum);
641
    seeker(possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
83,055✔
642
}
643

644
template <typename IStream>
645
void byte_reader<IStream>::do_rewind_bytes(size_t size) NOEXCEPT
866✔
646
{
647
    // Given that the stream size is unknown to the reader, the limit may be
648
    // arbitrarily high. This prevents an overflow if sum exceeds max_size_t.
649
    // max_size_t is presumed to exceed the range of IStream::pos_type, in 
650
    // which case this constraint does not affect the limiting behavior.
651
    remaining_ = ceilinged_add(remaining_, size);
1,728✔
652

653
    // Rewind past stream start invalidates stream unless size exceeds maximum.
654
    BC_ASSERT(size <= maximum);
655
    seeker(-possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
866✔
656
}
866✔
657

658
template <typename IStream>
659
bool byte_reader<IStream>::get_exhausted() const NOEXCEPT
195,778✔
660
{
661
    if (is_zero(remaining_))
195,778✔
662
        return true;
663

664
    // Empty behavior is broadly inconsistent across implementations.
665
    // It is also necessary to start many reads, including initial reads, with
666
    // an exhaustion check, which must be consistent and not state-changing.
667
    // A state change would preclude testing for errors after testing for end.
668
    // This method is const because it reliably creates no net state change.
669
    // Streams are valid unless read or peeked. Peek does not change stream
670
    // position, so it is used to force the stream into a failure state when
671
    // empty. peek past end always sets eofbit but peek state on empty is
672
    // inconsistent across streams, though a flag is always set. eofbit is set
673
    // by istringstream and badbit is set by boost.
674

675
    if (!valid())
194,776✔
676
        return true;
677

678
    // Peek to force error on eof, save condition, restore valid stream state.
679
    stream_.peek();
194,760✔
680
    const auto eof = !valid();
194,760✔
681
    stream_.clear();
194,760✔
682

683
    return eof;
194,760✔
684
}
685

686
// private
687
// ----------------------------------------------------------------------------
688
// These may only call other private methods (due to overriding).
689

690
template <typename IStream>
691
bool byte_reader<IStream>::valid() const NOEXCEPT
1,425,658✔
692
{
693
    // zero is the istream documented flag for no error.
694
    return stream_.rdstate() == IStream::goodbit;
1,425,658✔
695
}
696

697
template <typename IStream>
698
void byte_reader<IStream>::invalid() NOEXCEPT
12,290✔
699
{
700
    // If eofbit is set, failbit is generally set on all operations.
701
    // badbit is unrecoverable, set the others to ensure consistency.
702
    stream_.setstate(IStream::eofbit | IStream::failbit | IStream::badbit);
12,290✔
703
}
12,290✔
704

705
template <typename IStream>
706
void byte_reader<IStream>::validate() NOEXCEPT
579,065✔
707
{
708
    // Ensure that any failure in the call fully invalidates the stream/reader.
709
    // Some errors are recoverable, so a sequence of operations without testing
710
    // for validity could miss an error on intervening operations. For example,
711
    // seekg can reset eofbit and read past doesn't set badbit (recoverable).
712
    if (!valid())
579,065✔
713
        invalid();
12,257✔
714
}
579,065✔
715

716
template <typename IStream>
717
void byte_reader<IStream>::clear() NOEXCEPT
691✔
718
{
719
    // Does not reset the current position.
720
    stream_.clear();
691✔
721
}
691✔
722

723
template <typename IStream>
724
size_t byte_reader<IStream>::getter() NOEXCEPT
85,148✔
725
{
726
    static const auto failure = typename IStream::pos_type(-1);
85,148✔
727
    typename IStream::pos_type position;
728

729
    // Force these to be consistent, and avoid propagating exceptions.
730
    // Assuming behavior is consistent with seekg (as documented).
731
    // Returns current position on success and pos_type(-1) on failure.
732
    try
733
    {
734
        // This does not honor BOOST_EXCEPTION_DISABLE.
735
        position = stream_.tellg();
85,148✔
736
        validate();
85,148✔
737
    }
738
    catch (const typename IStream::failure&)
×
739
    {
740
        position = failure;
×
741
        invalid();
×
742
    }
743

744
    // Max size_t is presumed to exceed max IStream::pos_type.
745
    return position == failure ? zero :
85,148✔
746
        possible_narrow_and_sign_cast<size_t>(position);
85,148✔
747
}
748

749
template <typename IStream>
750
void byte_reader<IStream>::limit(size_t size) NOEXCEPT
1,004✔
751
{
752
    remaining_ = size;
1,004✔
753
}
754

755
template <typename IStream>
756
bool byte_reader<IStream>::limiter(size_t size) NOEXCEPT
574,739✔
757
{
758
    if (size > remaining_)
574,739✔
759
    {
760
        // Does not reset the current position or the remaining limit.
761
        invalidate();
14✔
762
        return true;
14✔
763
    }
764

765
    remaining_ -= size;
574,725✔
766
    return false;
574,725✔
767
}
768

769
template <typename IStream>
770
void byte_reader<IStream>::seeker(typename IStream::pos_type offset) NOEXCEPT
83,921✔
771
{
772
    // Force these to be consistent by treating zero seek as a no-op.
773
    // boost/istringstream both succeed on non-empty zero seek.
774
    // istringstream succeeds on empty zero seek, boost sets failbit.
775
    if (is_zero(offset))
83,921✔
776
        return;
777

778
    // Force these to be consistent, and avoid propagating exceptions.
779
    // istringstream sets failbit on non-empty over/underflow, boost throws.
780
    // boost/istringstream both set failbit on empty over/underflow.
781
    try
782
    {
783
        // This does not honor BOOST_EXCEPTION_DISABLE.
784
        stream_.seekg(offset, IStream::cur);
2,247✔
785
        validate();
2,247✔
786
    }
787
    catch (const typename IStream::failure&)
×
788
    {
789
        invalid();
×
790
    }
791
}
792

793
BC_POP_WARNING()
794

795
} // namespace system
796
} // namespace libbitcoin
797

798
#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