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

libbitcoin / libbitcoin-system / 9824402874

07 Jul 2024 03:52AM UTC coverage: 82.558% (-0.2%) from 82.725%
9824402874

Pull #1492

github

web-flow
Merge 826e50a22 into 3046b831f
Pull Request #1492: Add polymorphic allocating byte_reader methods.

53 of 78 new or added lines in 15 files covered. (67.95%)

12 existing lines in 1 file now uncovered.

9864 of 11948 relevant lines covered (82.56%)

4773499.46 hits per line

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

86.81
/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_resource*
42
byte_reader<IStream>::default_allocator() NOEXCEPT
13,179✔
43
{
44
    return std::pmr::get_default_resource();
13,179✔
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,
13,179✔
54
    memory_resource* allocator) NOEXCEPT
55
  : stream_(source),
13,179✔
56
    remaining_(system::maximum<size_t>),
13,179✔
57
    allocator_(allocator)
13,180✔
58
{
59
    ////BC_ASSERT_MSG(stream_.exceptions() == IStream::goodbit,
60
    ////    "Input stream must not be configured to throw exceptions.");
61
}
12,729✔
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,246✔
128
{
129
    Integer value{};
21,246✔
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,246✔
134
    return native_from_little_end(value);
21,246✔
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,186✔
151
{
152
    return read_little_endian<uint32_t>();
1,186✔
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,997✔
175
{
176
    return read_little_endian<uint64_t>();
19,997✔
177
}
178

179
template <typename IStream>
180
uint64_t byte_reader<IStream>::read_variable() NOEXCEPT
1,030✔
181
{
182
    switch (const auto value = read_byte())
1,030✔
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:
984✔
191
            return value;
984✔
192
    }
193
}
194

195
template <typename IStream>
196
size_t byte_reader<IStream>::read_size(size_t limit) NOEXCEPT
1,010✔
197
{
198
    const auto size = read_variable();
1,010✔
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,010✔
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,027✔
226
{
227
    uint8_t value = pad();
201,027✔
228
    do_read_bytes(&value, one);
201,027✔
229
    return value;
201,027✔
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
730✔
238
{
239
    // Truncated bytes are populated with 0x00.
240
    // Reader supports directly populating an array, this avoids a copy.
241
    data_array<Size> out{};
730✔
242
    do_read_bytes(out.data(), Size);
730✔
243
    return out;
730✔
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>
NEW
255
data_array_cptr<Size> byte_reader<IStream>::read_forward_cptr() NOEXCEPT
×
256
{
257
    // Truncated bytes are populated with 0x00.
258
    // Reader supports directly populating an array, this avoids a copy.
NEW
259
    const auto cptr = to_allocated<data_array<Size>>(allocator_);
×
260
    const auto ptr = const_cast<data_array<Size>*>(cptr.get());
NEW
261
    do_read_bytes(ptr->data(), Size);
×
NEW
262
    return cptr;
×
263
}
264

265
template <typename IStream>
266
template <size_t Size>
267
data_array_cptr<Size> byte_reader<IStream>::read_reverse_cptr() NOEXCEPT
268
{
269
    const auto cptr = read_forward_cptr<Size>();
270
    const auto ptr = const_cast<data_array<Size>*>(cptr.get());
271
    std::reverse(ptr->begin(), ptr->end());
272
    return cptr;
273
}
274

275
template <typename IStream>
276
mini_hash byte_reader<IStream>::read_mini_hash() NOEXCEPT
8✔
277
{
278
    return read_forward<mini_hash_size>();
8✔
279
}
280

281
template <typename IStream>
282
short_hash byte_reader<IStream>::read_short_hash() NOEXCEPT
4✔
283
{
284
    return read_forward<short_hash_size>();
4✔
285
}
286

287
template <typename IStream>
288
hash_digest byte_reader<IStream>::read_hash() NOEXCEPT
660✔
289
{
290
    return read_forward<hash_size>();
660✔
291
}
292

293
template <typename IStream>
294
long_hash byte_reader<IStream>::read_long_hash() NOEXCEPT
4✔
295
{
296
    return read_forward<long_hash_size>();
4✔
297
}
298

299
template <typename IStream>
NEW
UNCOV
300
mini_hash_cptr byte_reader<IStream>::read_mini_hash_cptr() NOEXCEPT
×
301
{
NEW
UNCOV
302
    return read_forward_cptr<mini_hash_size>();
×
303
}
304

305
template <typename IStream>
NEW
UNCOV
306
short_hash_cptr byte_reader<IStream>::read_short_hash_cptr() NOEXCEPT
×
307
{
NEW
UNCOV
308
    return read_forward_cptr<short_hash_size>();
×
309
}
310

311
template <typename IStream>
NEW
312
hash_cptr byte_reader<IStream>::read_hash_cptr() NOEXCEPT
×
313
{
NEW
314
    return read_forward_cptr<hash_size>();
×
315
}
316

317
template <typename IStream>
NEW
UNCOV
318
long_hash_cptr byte_reader<IStream>::read_long_hash_cptr() NOEXCEPT
×
319
{
NEW
320
    return read_forward_cptr<long_hash_size>();
×
321
}
322

323
// byte vectors
324
// ----------------------------------------------------------------------------
325

326
template <typename IStream>
327
data_chunk byte_reader<IStream>::read_bytes() NOEXCEPT
39✔
328
{
329
    // Count bytes to the end, avoids push_back reallocations.
330
    size_t size{};
331
    while (!get_exhausted())
416✔
332
    {
333
        ++size;
377✔
334
        skip_byte();
377✔
335
    };
336

337
    rewind_bytes(size);
39✔
338
    return read_bytes(size);
39✔
339
}
340

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

NEW
352
    rewind_bytes(size);
×
NEW
353
    return read_bytes_cptr(size);
×
354
}
355

356
template <typename IStream>
357
data_chunk byte_reader<IStream>::read_bytes(size_t size) NOEXCEPT
82,871✔
358
{
359
    if (is_zero(size))
82,863✔
360
        return {};
81,693✔
361

362
    // This allows caller to read an invalid stream without allocation.
363
    if (!valid())
1,178✔
UNCOV
364
        return{};
×
365

366
    data_chunk out(size);
1,178✔
367
    do_read_bytes(out.data(), size);
1,178✔
368
    return out;
369
}
370

371
template <typename IStream>
NEW
UNCOV
372
chunk_cptr byte_reader<IStream>::read_bytes_cptr(size_t size) NOEXCEPT
×
373
{
NEW
374
    if (is_zero(size))
×
375
        return {};
376

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

NEW
381
    const auto cptr = to_allocated<data_chunk>(allocator_, size);
×
382
    const auto ptr = const_cast<data_chunk*>(cptr.get());
NEW
383
    do_read_bytes(ptr->data(), size);
×
384
    return cptr;
385
}
386

387
template <typename IStream>
388
void byte_reader<IStream>::read_bytes(uint8_t* buffer, size_t size) NOEXCEPT
21,900✔
389
{
390
    do_read_bytes(buffer, size);
21,900✔
391
}
21,900✔
392

393
// strings
394
// ----------------------------------------------------------------------------
395

396
template <typename IStream>
397
std::string byte_reader<IStream>::read_string(size_t limit) NOEXCEPT
16✔
398
{
399
    return read_string_buffer(read_size(limit));
16✔
400
}
401

402
template <typename IStream>
403
std::string byte_reader<IStream>::read_string_buffer(size_t size) NOEXCEPT
40✔
404
{
405
    // Isolating get_exhausted to first call is an optimization (must clear).
406
    if (get_exhausted())
40✔
407
        return {};
408

409
    // This will produce one (zero) terminating byte if size exceeds available.
410
    std::string out{};
411
    out.reserve(add1(size));
36✔
412
    while (!is_zero(size--) && valid())
236✔
413
        out.push_back(read_byte());
200✔
414

415
    // Removes zero and all after, required for bitcoin string deserialization.
416
    const auto position = out.find('\0');
36✔
417
    out.resize(position == std::string::npos ? out.size() : position);
68✔
418
    ////out.shrink_to_fit();
419

420
    clear();
36✔
421
    return out;
36✔
422
}
423

424
// streams
425
// ----------------------------------------------------------------------------
426

427
template <typename IStream>
428
std::ostream& byte_reader<IStream>::read(std::ostream& out) NOEXCEPT
8✔
429
{
430
    // This creates an intermediate buffer the size of the stream.
431
    // This is presumed to be more optimal than looping individual bytes.
432
    byte_writer<std::ostream>(out).write_bytes(read_bytes());
8✔
433
    return out;
8✔
434
}
435

436
// context
437
// ----------------------------------------------------------------------------
438

439
template <typename IStream>
440
void byte_reader<IStream>::skip_byte() NOEXCEPT
547✔
441
{
442
    do_skip_bytes(one);
547✔
443
}
547✔
444

445
template <typename IStream>
446
void byte_reader<IStream>::skip_bytes(size_t size) NOEXCEPT
82,576✔
447
{
448
    do_skip_bytes(size);
82,576✔
449
}
82,576✔
450

451
template <typename IStream>
452
void byte_reader<IStream>::skip_variable() NOEXCEPT
24✔
453
{
454
    switch (read_byte())
24✔
455
    {
456
        case varint_eight_bytes:
8✔
457
            do_skip_bytes(8);
8✔
458
            return;
8✔
459
        case varint_four_bytes:
4✔
460
            do_skip_bytes(4);
4✔
461
            return;
4✔
462
        case varint_two_bytes:
4✔
463
            do_skip_bytes(2);
4✔
464
            return;
4✔
465
        default:
466
            return;
467
    }
468
}
469

470
template <typename IStream>
471
void byte_reader<IStream>::rewind_byte() NOEXCEPT
4✔
472
{
473
    do_rewind_bytes(one);
4✔
474
}
4✔
475

476
template <typename IStream>
477
void byte_reader<IStream>::rewind_bytes(size_t size) NOEXCEPT
63✔
478
{
479
    do_rewind_bytes(size);
63✔
480
}
63✔
481

482
template <typename IStream>
483
void byte_reader<IStream>::set_position(size_t absolute) NOEXCEPT
651✔
484
{
485
    // Clear a presumed error state following a read overflow.
486
    clear();
651✔
487

488
    // This allows conversion of and absolute to relative position.
489
    const auto position = get_read_position();
651✔
490

491
    if (absolute == position)
651✔
492
        return;
493

494
    if (absolute > position)
613✔
495
        do_skip_bytes(absolute - position);
6✔
496
    else
497
        do_rewind_bytes(position - absolute);
607✔
498
}
499

500
template <typename IStream>
501
bool byte_reader<IStream>::is_exhausted() const NOEXCEPT
371,804✔
502
{
503
    // True if invalid or if no bytes remain in the stream.
504
    return get_exhausted();
371,804✔
505
}
506

507
// control
508
// ----------------------------------------------------------------------------
509
// These only call non-virtual (private) methods.
510

511
template <typename IStream>
512
size_t byte_reader<IStream>::get_read_position() NOEXCEPT
85,122✔
513
{
514
    return getter();
85,122✔
515
}
516

517
template <typename IStream>
518
void byte_reader<IStream>::set_limit(size_t size) NOEXCEPT
996✔
519
{
520
    limit(size);
521
}
994✔
522

523
template <typename IStream>
524
void byte_reader<IStream>::invalidate() NOEXCEPT
29✔
525
{
526
    // Permanently invalidate the stream/reader.
527
    invalid();
29✔
528
}
17✔
529

530
template <typename IStream>
531
byte_reader<IStream>::operator bool() const NOEXCEPT
84,284✔
532
{
533
    // True if any call created an error state, even if there have been
534
    // subsequent calls, or if any error state preexists on the stream.
535
    return valid();
84,284✔
536
}
537

538
// This should not be necessary with bool() defined, but it is.
539
template <typename IStream>
540
bool byte_reader<IStream>::operator!() const NOEXCEPT
371,230✔
541
{
542
    return !valid();
371,230✔
543
}
544

545
// protected virtual
546
// ----------------------------------------------------------------------------
547
// These may only call non-virtual (private) methods (due to overriding).
548

549
// Suppress istream members may throw inside NOEXCEPT.
550
// The intended behavior in this case is program abort.
551
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
552

553
template <typename IStream>
554
uint8_t byte_reader<IStream>::do_peek_byte() NOEXCEPT
50✔
555
{
556
    if (limiter(one))
50✔
557
        return pad();
558

559
    // This sets eofbit (or badbit) on empty and eofbit if otherwise at end.
560
    // eofbit does not cause !!eofbit == true, but badbit does, so we validate
561
    // the call the achieve consistent behavior. The reader will be invalid if
562
    // the stream is peeked past end, including when empty.
563
    const auto value =
564
        possible_narrow_and_sign_cast<uint8_t>(stream_.peek());
48✔
565

566
    validate();
48✔
567
    return valid() ? value : pad();
48✔
568
}
569

570
template <typename IStream>
571
void byte_reader<IStream>::do_read_bytes(uint8_t* buffer, size_t size) NOEXCEPT
491,551✔
572
{
573
    // Limited reads are not partially filled or padded.
574
    if (limiter(size))
491,551✔
575
        return;
576

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

583
    // Read past stream end invalidates stream unless size exceeds maximum.
584
    BC_ASSERT(size <= maximum);
585
    stream_.read(pointer_cast<char>(buffer),
491,545✔
586
        possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
587

588
    validate();
491,545✔
589
}
590

591
template <typename IStream>
592
void byte_reader<IStream>::do_skip_bytes(size_t size) NOEXCEPT
83,041✔
593
{
594
    if (limiter(size))
83,041✔
595
        return;
596

597
    // Skip past stream end invalidates stream unless size exceeds maximum.
598
    BC_ASSERT(size <= maximum);
599
    seeker(possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
83,035✔
600
}
601

602
template <typename IStream>
603
void byte_reader<IStream>::do_rewind_bytes(size_t size) NOEXCEPT
857✔
604
{
605
    // Given that the stream size is unknown to the reader, the limit may be
606
    // arbitrarily high. This prevents an overflow if sum exceeds max_size_t.
607
    // max_size_t is presumed to exceed the range of IStream::pos_type, in 
608
    // which case this constraint does not affect the limiting behavior.
609
    remaining_ = ceilinged_add(remaining_, size);
1,710✔
610

611
    // Rewind past stream start invalidates stream unless size exceeds maximum.
612
    BC_ASSERT(size <= maximum);
613
    seeker(-possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
857✔
614
}
857✔
615

616
template <typename IStream>
617
bool byte_reader<IStream>::get_exhausted() const NOEXCEPT
195,742✔
618
{
619
    if (is_zero(remaining_))
195,742✔
620
        return true;
621

622
    // Empty behavior is broadly inconsistent across implementations.
623
    // It is also necessary to start many reads, including initial reads, with
624
    // an exhaustion check, which must be consistent and not state-changing.
625
    // A state change would preclude testing for errors after testing for end.
626
    // This method is const because it reliably creates no net state change.
627
    // Streams are valid unless read or peeked. Peek does not change stream
628
    // position, so it is used to force the stream into a failure state when
629
    // empty. peek past end always sets eofbit but peek state on empty is
630
    // inconsistent across streams, though a flag is always set. eofbit is set
631
    // by istringstream and badbit is set by boost.
632

633
    if (!valid())
194,748✔
634
        return true;
635

636
    // Peek to force error on eof, save condition, restore valid stream state.
637
    stream_.peek();
194,734✔
638
    const auto eof = !valid();
194,734✔
639
    stream_.clear();
194,734✔
640

641
    return eof;
194,734✔
642
}
643

644
// private
645
// ----------------------------------------------------------------------------
646
// These may only call other private methods (due to overriding).
647

648
template <typename IStream>
649
bool byte_reader<IStream>::valid() const NOEXCEPT
1,425,361✔
650
{
651
    // zero is the istream documented flag for no error.
652
    return stream_.rdstate() == IStream::goodbit;
1,425,361✔
653
}
654

655
template <typename IStream>
656
void byte_reader<IStream>::invalid() NOEXCEPT
12,281✔
657
{
658
    // If eofbit is set, failbit is generally set on all operations.
659
    // badbit is unrecoverable, set the others to ensure consistency.
660
    stream_.setstate(IStream::eofbit | IStream::failbit | IStream::badbit);
12,281✔
661
}
12,281✔
662

663
template <typename IStream>
664
void byte_reader<IStream>::validate() NOEXCEPT
578,939✔
665
{
666
    // Ensure that any failure in the call fully invalidates the stream/reader.
667
    // Some errors are recoverable, so a sequence of operations without testing
668
    // for validity could miss an error on intervening operations. For example,
669
    // seekg can reset eofbit and read past doesn't set badbit (recoverable).
670
    if (!valid())
578,939✔
671
        invalid();
12,252✔
672
}
578,939✔
673

674
template <typename IStream>
675
void byte_reader<IStream>::clear() NOEXCEPT
687✔
676
{
677
    // Does not reset the current position.
678
    stream_.clear();
687✔
679
}
687✔
680

681
template <typename IStream>
682
size_t byte_reader<IStream>::getter() NOEXCEPT
85,122✔
683
{
684
    static const auto failure = typename IStream::pos_type(-1);
85,122✔
685
    typename IStream::pos_type position;
686

687
    // Force these to be consistent, and avoid propagating exceptions.
688
    // Assuming behavior is consistent with seekg (as documented).
689
    // Returns current position on success and pos_type(-1) on failure.
690
    try
691
    {
692
        // This does not honor BOOST_EXCEPTION_DISABLE.
693
        position = stream_.tellg();
85,122✔
694
        validate();
85,122✔
695
    }
UNCOV
696
    catch (const typename IStream::failure&)
×
697
    {
UNCOV
698
        position = failure;
×
UNCOV
699
        invalid();
×
700
    }
701

702
    // Max size_t is presumed to exceed max IStream::pos_type.
703
    return position == failure ? zero :
85,122✔
704
        possible_narrow_and_sign_cast<size_t>(position);
85,122✔
705
}
706

707
template <typename IStream>
708
void byte_reader<IStream>::limit(size_t size) NOEXCEPT
996✔
709
{
710
    remaining_ = size;
996✔
711
}
712

713
template <typename IStream>
714
bool byte_reader<IStream>::limiter(size_t size) NOEXCEPT
574,642✔
715
{
716
    if (size > remaining_)
574,642✔
717
    {
718
        // Does not reset the current position or the remaining limit.
719
        invalidate();
14✔
720
        return true;
14✔
721
    }
722

723
    remaining_ -= size;
574,628✔
724
    return false;
574,628✔
725
}
726

727
template <typename IStream>
728
void byte_reader<IStream>::seeker(typename IStream::pos_type offset) NOEXCEPT
83,892✔
729
{
730
    // Force these to be consistent by treating zero seek as a no-op.
731
    // boost/istringstream both succeed on non-empty zero seek.
732
    // istringstream succeeds on empty zero seek, boost sets failbit.
733
    if (is_zero(offset))
83,892✔
734
        return;
735

736
    // Force these to be consistent, and avoid propagating exceptions.
737
    // istringstream sets failbit on non-empty over/underflow, boost throws.
738
    // boost/istringstream both set failbit on empty over/underflow.
739
    try
740
    {
741
        // This does not honor BOOST_EXCEPTION_DISABLE.
742
        stream_.seekg(offset, IStream::cur);
2,224✔
743
        validate();
2,224✔
744
    }
UNCOV
745
    catch (const typename IStream::failure&)
×
746
    {
UNCOV
747
        invalid();
×
748
    }
749
}
750

751
BC_POP_WARNING()
752

753
} // namespace system
754
} // namespace libbitcoin
755

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