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

libbitcoin / libbitcoin-system / 9833492110

08 Jul 2024 04:22AM UTC coverage: 82.783% (+0.3%) from 82.53%
9833492110

Pull #1495

github

web-flow
Merge 41a29b9bf into 5d8dc903b
Pull Request #1495: Add byte_reader tests for ptr returns, test memory functions, delint.

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
    // Truncated bytes are populated with 0x00.
268
    // Reader supports directly populating an array, this avoids a copy.
269
    const auto ptr = to_non_const_raw_ptr(cptr);
270
    do_read_bytes(ptr->data(), Size);
11✔
271
    if (!valid())
11✔
272
        return {};
273

274
    return cptr;
275
}
276

277
template <typename IStream>
278
template <size_t Size>
279
data_array_cptr<Size> byte_reader<IStream>::read_reverse_cptr() NOEXCEPT
3✔
280
{
281
    const auto cptr = read_forward_cptr<Size>();
3✔
282
    if (!cptr)
3✔
283
        return cptr;
284

285
    const auto ptr = to_non_const_raw_ptr(cptr);
286
    std::reverse(ptr->begin(), ptr->end());
1✔
287
    return cptr;
1✔
288
}
289

290
template <typename IStream>
291
mini_hash byte_reader<IStream>::read_mini_hash() NOEXCEPT
8✔
292
{
293
    return read_forward<mini_hash_size>();
8✔
294
}
295

296
template <typename IStream>
297
short_hash byte_reader<IStream>::read_short_hash() NOEXCEPT
4✔
298
{
299
    return read_forward<short_hash_size>();
4✔
300
}
301

302
template <typename IStream>
303
hash_digest byte_reader<IStream>::read_hash() NOEXCEPT
666✔
304
{
305
    return read_forward<hash_size>();
666✔
306
}
307

308
template <typename IStream>
309
long_hash byte_reader<IStream>::read_long_hash() NOEXCEPT
4✔
310
{
311
    return read_forward<long_hash_size>();
4✔
312
}
313

314
template <typename IStream>
315
mini_hash_cptr byte_reader<IStream>::read_mini_hash_cptr() NOEXCEPT
2✔
316
{
317
    return read_forward_cptr<mini_hash_size>();
2✔
318
}
319

320
template <typename IStream>
321
short_hash_cptr byte_reader<IStream>::read_short_hash_cptr() NOEXCEPT
1✔
322
{
323
    return read_forward_cptr<short_hash_size>();
1✔
324
}
325

326
template <typename IStream>
327
hash_cptr byte_reader<IStream>::read_hash_cptr() NOEXCEPT
1✔
328
{
329
    return read_forward_cptr<hash_size>();
1✔
330
}
331

332
template <typename IStream>
333
long_hash_cptr byte_reader<IStream>::read_long_hash_cptr() NOEXCEPT
1✔
334
{
335
    return read_forward_cptr<long_hash_size>();
1✔
336
}
337

338
// byte vectors
339
// ----------------------------------------------------------------------------
340

341
template <typename IStream>
342
data_chunk byte_reader<IStream>::read_bytes() NOEXCEPT
40✔
343
{
344
    // Count bytes to the end, avoids push_back reallocations.
345
    size_t size{};
346
    while (!get_exhausted())
417✔
347
    {
348
        ++size;
377✔
349
        skip_byte();
377✔
350
    };
351

352
    rewind_bytes(size);
40✔
353
    return read_bytes(size);
40✔
354
}
355

356
template <typename IStream>
357
chunk_cptr byte_reader<IStream>::read_bytes_cptr() NOEXCEPT
4✔
358
{
359
    // Count bytes to the end, avoids push_back reallocations.
360
    size_t size{};
361
    while (!get_exhausted())
7✔
362
    {
363
        ++size;
3✔
364
        skip_byte();
3✔
365
    };
366

367
    rewind_bytes(size);
4✔
368
    return read_bytes_cptr(size);
4✔
369
}
370

371
template <typename IStream>
372
data_chunk byte_reader<IStream>::read_bytes(size_t size) NOEXCEPT
82,883✔
373
{
374
    if (is_zero(size))
82,874✔
375
        return {};
81,697✔
376

377
    // This allows caller to read an invalid stream without allocation.
378
    if (!valid())
1,186✔
NEW
379
        return {};
×
380

381
    data_chunk out(size);
1,186✔
382
    do_read_bytes(out.data(), size);
1,186✔
383
    return out;
384
}
385

386
template <typename IStream>
387
chunk_cptr byte_reader<IStream>::read_bytes_cptr(size_t size) NOEXCEPT
10✔
388
{
389
    // This allows caller to read an invalid stream without allocation.
390
    if (!valid())
10✔
391
        return {};
392

393
    const auto cptr = to_allocated<data_chunk>(allocator_, size);
8✔
394
    if (!cptr)
8✔
395
    {
UNCOV
396
        invalidate();
×
397
        return cptr;
398
    }
399

400
    if (is_zero(size))
8✔
401
        return cptr;
402

403
    BC_PUSH_WARNING(NO_UNGUARDED_POINTERS)
404
    const auto ptr = const_cast<data_chunk*>(cptr.get());
405
    BC_POP_WARNING()
406

407
    do_read_bytes(ptr->data(), size);
4✔
408
    if (!valid())
4✔
409
        return {};
410

411
    return cptr;
412
}
413

414
template <typename IStream>
415
void byte_reader<IStream>::read_bytes(uint8_t* buffer, size_t size) NOEXCEPT
21,918✔
416
{
417
    do_read_bytes(buffer, size);
21,918✔
418
}
21,918✔
419

420
// strings
421
// ----------------------------------------------------------------------------
422

423
template <typename IStream>
424
std::string byte_reader<IStream>::read_string(size_t limit) NOEXCEPT
16✔
425
{
426
    return read_string_buffer(read_size(limit));
16✔
427
}
428

429
template <typename IStream>
430
std::string byte_reader<IStream>::read_string_buffer(size_t size) NOEXCEPT
40✔
431
{
432
    // Isolating get_exhausted to first call is an optimization (must clear).
433
    if (get_exhausted())
40✔
434
        return {};
435

436
    // This will produce one (zero) terminating byte if size exceeds available.
437
    std::string out{};
438
    out.reserve(add1(size));
36✔
439
    while (!is_zero(size--) && valid())
236✔
440
        out.push_back(read_byte());
200✔
441

442
    // Removes zero and all after, required for bitcoin string deserialization.
443
    const auto position = out.find('\0');
36✔
444
    out.resize(position == std::string::npos ? out.size() : position);
68✔
445
    ////out.shrink_to_fit();
446

447
    clear();
36✔
448
    return out;
36✔
449
}
450

451
// streams
452
// ----------------------------------------------------------------------------
453

454
template <typename IStream>
455
std::ostream& byte_reader<IStream>::read(std::ostream& out) NOEXCEPT
8✔
456
{
457
    // This creates an intermediate buffer the size of the stream.
458
    // This is presumed to be more optimal than looping individual bytes.
459
    byte_writer<std::ostream>(out).write_bytes(read_bytes());
8✔
460
    return out;
8✔
461
}
462

463
// context
464
// ----------------------------------------------------------------------------
465

466
template <typename IStream>
467
void byte_reader<IStream>::skip_byte() NOEXCEPT
555✔
468
{
469
    do_skip_bytes(one);
555✔
470
}
555✔
471

472
template <typename IStream>
473
void byte_reader<IStream>::skip_bytes(size_t size) NOEXCEPT
82,588✔
474
{
475
    do_skip_bytes(size);
82,588✔
476
}
82,588✔
477

478
template <typename IStream>
479
void byte_reader<IStream>::skip_variable() NOEXCEPT
24✔
480
{
481
    switch (read_byte())
24✔
482
    {
483
        case varint_eight_bytes:
8✔
484
            do_skip_bytes(8);
8✔
485
            return;
8✔
486
        case varint_four_bytes:
4✔
487
            do_skip_bytes(4);
4✔
488
            return;
4✔
489
        case varint_two_bytes:
4✔
490
            do_skip_bytes(2);
4✔
491
            return;
4✔
492
        default:
493
            return;
494
    }
495
}
496

497
template <typename IStream>
498
void byte_reader<IStream>::rewind_byte() NOEXCEPT
4✔
499
{
500
    do_rewind_bytes(one);
4✔
501
}
4✔
502

503
template <typename IStream>
504
void byte_reader<IStream>::rewind_bytes(size_t size) NOEXCEPT
68✔
505
{
506
    do_rewind_bytes(size);
68✔
507
}
68✔
508

509
template <typename IStream>
510
void byte_reader<IStream>::set_position(size_t absolute) NOEXCEPT
655✔
511
{
512
    // Clear a presumed error state following a read overflow.
513
    clear();
655✔
514

515
    // This allows conversion of and absolute to relative position.
516
    const auto position = get_read_position();
655✔
517

518
    if (absolute == position)
655✔
519
        return;
520

521
    if (absolute > position)
617✔
522
        do_skip_bytes(absolute - position);
6✔
523
    else
524
        do_rewind_bytes(position - absolute);
611✔
525
}
526

527
template <typename IStream>
528
bool byte_reader<IStream>::is_exhausted() const NOEXCEPT
371,832✔
529
{
530
    // True if invalid or if no bytes remain in the stream.
531
    return get_exhausted();
371,832✔
532
}
533

534
// control
535
// ----------------------------------------------------------------------------
536
// These only call non-virtual (private) methods.
537

538
template <typename IStream>
539
size_t byte_reader<IStream>::get_read_position() NOEXCEPT
85,148✔
540
{
541
    return getter();
85,148✔
542
}
543

544
template <typename IStream>
545
void byte_reader<IStream>::set_limit(size_t size) NOEXCEPT
1,004✔
546
{
547
    limit(size);
548
}
1,002✔
549

550
template <typename IStream>
551
void byte_reader<IStream>::invalidate() NOEXCEPT
33✔
552
{
553
    // Permanently invalidate the stream/reader.
554
    invalid();
33✔
555
}
17✔
556

557
template <typename IStream>
558
byte_reader<IStream>::operator bool() const NOEXCEPT
84,310✔
559
{
560
    // True if any call created an error state, even if there have been
561
    // subsequent calls, or if any error state preexists on the stream.
562
    return valid();
84,310✔
563
}
564

565
// This should not be necessary with bool() defined, but it is.
566
template <typename IStream>
567
bool byte_reader<IStream>::operator!() const NOEXCEPT
371,277✔
568
{
569
    return !valid();
371,277✔
570
}
571

572
template <typename IStream>
573
const typename byte_reader<IStream>::memory_allocator&
574
byte_reader<IStream>::allocator() const NOEXCEPT
2✔
575
{
NEW
576
    return allocator_;
×
577
}
578

579
// protected virtual
580
// ----------------------------------------------------------------------------
581
// These may only call non-virtual (private) methods (due to overriding).
582

583
// Suppress istream members may throw inside NOEXCEPT.
584
// The intended behavior in this case is program abort.
585
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
586

587
template <typename IStream>
588
uint8_t byte_reader<IStream>::do_peek_byte() NOEXCEPT
50✔
589
{
590
    if (limiter(one))
50✔
591
        return pad();
592

593
    // This sets eofbit (or badbit) on empty and eofbit if otherwise at end.
594
    // eofbit does not cause !!eofbit == true, but badbit does, so we validate
595
    // the call the achieve consistent behavior. The reader will be invalid if
596
    // the stream is peeked past end, including when empty.
597
    const auto value =
598
        possible_narrow_and_sign_cast<uint8_t>(stream_.peek());
48✔
599

600
    validate();
48✔
601
    return valid() ? value : pad();
48✔
602
}
603

604
template <typename IStream>
605
void byte_reader<IStream>::do_read_bytes(uint8_t* buffer, size_t size) NOEXCEPT
491,628✔
606
{
607
    // Limited reads are not partially filled or padded.
608
    if (limiter(size))
491,628✔
609
        return;
610

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

617
    // Read past stream end invalidates stream unless size exceeds maximum.
618
    BC_ASSERT(size <= maximum);
619
    stream_.read(pointer_cast<char>(buffer),
491,622✔
620
        possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
621

622
    validate();
491,622✔
623
}
624

625
template <typename IStream>
626
void byte_reader<IStream>::do_skip_bytes(size_t size) NOEXCEPT
83,061✔
627
{
628
    if (limiter(size))
83,061✔
629
        return;
630

631
    // Skip past stream end invalidates stream unless size exceeds maximum.
632
    BC_ASSERT(size <= maximum);
633
    seeker(possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
83,055✔
634
}
635

636
template <typename IStream>
637
void byte_reader<IStream>::do_rewind_bytes(size_t size) NOEXCEPT
866✔
638
{
639
    // Given that the stream size is unknown to the reader, the limit may be
640
    // arbitrarily high. This prevents an overflow if sum exceeds max_size_t.
641
    // max_size_t is presumed to exceed the range of IStream::pos_type, in 
642
    // which case this constraint does not affect the limiting behavior.
643
    remaining_ = ceilinged_add(remaining_, size);
1,728✔
644

645
    // Rewind past stream start invalidates stream unless size exceeds maximum.
646
    BC_ASSERT(size <= maximum);
647
    seeker(-possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
866✔
648
}
866✔
649

650
template <typename IStream>
651
bool byte_reader<IStream>::get_exhausted() const NOEXCEPT
195,778✔
652
{
653
    if (is_zero(remaining_))
195,778✔
654
        return true;
655

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

667
    if (!valid())
194,776✔
668
        return true;
669

670
    // Peek to force error on eof, save condition, restore valid stream state.
671
    stream_.peek();
194,760✔
672
    const auto eof = !valid();
194,760✔
673
    stream_.clear();
194,760✔
674

675
    return eof;
194,760✔
676
}
677

678
// private
679
// ----------------------------------------------------------------------------
680
// These may only call other private methods (due to overriding).
681

682
template <typename IStream>
683
bool byte_reader<IStream>::valid() const NOEXCEPT
1,425,658✔
684
{
685
    // zero is the istream documented flag for no error.
686
    return stream_.rdstate() == IStream::goodbit;
1,425,658✔
687
}
688

689
template <typename IStream>
690
void byte_reader<IStream>::invalid() NOEXCEPT
12,290✔
691
{
692
    // If eofbit is set, failbit is generally set on all operations.
693
    // badbit is unrecoverable, set the others to ensure consistency.
694
    stream_.setstate(IStream::eofbit | IStream::failbit | IStream::badbit);
12,290✔
695
}
12,290✔
696

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

708
template <typename IStream>
709
void byte_reader<IStream>::clear() NOEXCEPT
691✔
710
{
711
    // Does not reset the current position.
712
    stream_.clear();
691✔
713
}
691✔
714

715
template <typename IStream>
716
size_t byte_reader<IStream>::getter() NOEXCEPT
85,148✔
717
{
718
    static const auto failure = typename IStream::pos_type(-1);
85,148✔
719
    typename IStream::pos_type position;
720

721
    // Force these to be consistent, and avoid propagating exceptions.
722
    // Assuming behavior is consistent with seekg (as documented).
723
    // Returns current position on success and pos_type(-1) on failure.
724
    try
725
    {
726
        // This does not honor BOOST_EXCEPTION_DISABLE.
727
        position = stream_.tellg();
85,148✔
728
        validate();
85,148✔
729
    }
730
    catch (const typename IStream::failure&)
×
731
    {
732
        position = failure;
×
733
        invalid();
×
734
    }
735

736
    // Max size_t is presumed to exceed max IStream::pos_type.
737
    return position == failure ? zero :
85,148✔
738
        possible_narrow_and_sign_cast<size_t>(position);
85,148✔
739
}
740

741
template <typename IStream>
742
void byte_reader<IStream>::limit(size_t size) NOEXCEPT
1,004✔
743
{
744
    remaining_ = size;
1,004✔
745
}
746

747
template <typename IStream>
748
bool byte_reader<IStream>::limiter(size_t size) NOEXCEPT
574,739✔
749
{
750
    if (size > remaining_)
574,739✔
751
    {
752
        // Does not reset the current position or the remaining limit.
753
        invalidate();
14✔
754
        return true;
14✔
755
    }
756

757
    remaining_ -= size;
574,725✔
758
    return false;
574,725✔
759
}
760

761
template <typename IStream>
762
void byte_reader<IStream>::seeker(typename IStream::pos_type offset) NOEXCEPT
83,921✔
763
{
764
    // Force these to be consistent by treating zero seek as a no-op.
765
    // boost/istringstream both succeed on non-empty zero seek.
766
    // istringstream succeeds on empty zero seek, boost sets failbit.
767
    if (is_zero(offset))
83,921✔
768
        return;
769

770
    // Force these to be consistent, and avoid propagating exceptions.
771
    // istringstream sets failbit on non-empty over/underflow, boost throws.
772
    // boost/istringstream both set failbit on empty over/underflow.
773
    try
774
    {
775
        // This does not honor BOOST_EXCEPTION_DISABLE.
776
        stream_.seekg(offset, IStream::cur);
2,247✔
777
        validate();
2,247✔
778
    }
779
    catch (const typename IStream::failure&)
×
780
    {
781
        invalid();
×
782
    }
783
}
784

785
BC_POP_WARNING()
786

787
} // namespace system
788
} // namespace libbitcoin
789

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