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

libbitcoin / libbitcoin-system / 9830055600

07 Jul 2024 08:18PM UTC coverage: 82.783% (+0.3%) from 82.53%
9830055600

Pull #1495

github

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

3 of 4 new or added lines in 2 files covered. (75.0%)

8 existing lines in 1 file now uncovered.

9900 of 11959 relevant lines covered (82.78%)

4769108.87 hits per line

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

95.88
/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,201✔
43
{
44
    return std::pmr::get_default_resource();
13,202✔
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,202✔
54
    memory_resource* allocator) NOEXCEPT
55
  : stream_(source),
13,202✔
56
    remaining_(system::maximum<size_t>),
13,202✔
57
    allocator_(allocator)
13,202✔
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>
255
data_array_cptr<Size> byte_reader<IStream>::read_forward_cptr() NOEXCEPT
11✔
256
{
257
    // Truncated bytes are populated with 0x00.
258
    // Reader supports directly populating an array, this avoids a copy.
259
    const auto cptr = to_allocated<data_array<Size>>(allocator_);
11✔
260
    if (const auto ptr = const_cast<data_array<Size>*>(cptr.get()))
11✔
261
        do_read_bytes(ptr->data(), Size);
11✔
262
    else
UNCOV
263
        invalidate();
×
264

265
    return cptr;
11✔
266
}
267

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

278
template <typename IStream>
279
mini_hash byte_reader<IStream>::read_mini_hash() NOEXCEPT
8✔
280
{
281
    return read_forward<mini_hash_size>();
8✔
282
}
283

284
template <typename IStream>
285
short_hash byte_reader<IStream>::read_short_hash() NOEXCEPT
4✔
286
{
287
    return read_forward<short_hash_size>();
4✔
288
}
289

290
template <typename IStream>
291
hash_digest byte_reader<IStream>::read_hash() NOEXCEPT
660✔
292
{
293
    return read_forward<hash_size>();
660✔
294
}
295

296
template <typename IStream>
297
long_hash byte_reader<IStream>::read_long_hash() NOEXCEPT
4✔
298
{
299
    return read_forward<long_hash_size>();
4✔
300
}
301

302
template <typename IStream>
303
mini_hash_cptr byte_reader<IStream>::read_mini_hash_cptr() NOEXCEPT
2✔
304
{
305
    return read_forward_cptr<mini_hash_size>();
2✔
306
}
307

308
template <typename IStream>
309
short_hash_cptr byte_reader<IStream>::read_short_hash_cptr() NOEXCEPT
1✔
310
{
311
    return read_forward_cptr<short_hash_size>();
1✔
312
}
313

314
template <typename IStream>
315
hash_cptr byte_reader<IStream>::read_hash_cptr() NOEXCEPT
1✔
316
{
317
    return read_forward_cptr<hash_size>();
1✔
318
}
319

320
template <typename IStream>
321
long_hash_cptr byte_reader<IStream>::read_long_hash_cptr() NOEXCEPT
1✔
322
{
323
    return read_forward_cptr<long_hash_size>();
1✔
324
}
325

326
// byte vectors
327
// ----------------------------------------------------------------------------
328

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

340
    rewind_bytes(size);
40✔
341
    return read_bytes(size);
40✔
342
}
343

344
template <typename IStream>
345
chunk_cptr byte_reader<IStream>::read_bytes_cptr() NOEXCEPT
4✔
346
{
347
    // Count bytes to the end, avoids push_back reallocations.
348
    size_t size{};
349
    while (!get_exhausted())
7✔
350
    {
351
        ++size;
3✔
352
        skip_byte();
3✔
353
    };
354

355
    rewind_bytes(size);
4✔
356
    return read_bytes_cptr(size);
4✔
357
}
358

359
template <typename IStream>
360
data_chunk byte_reader<IStream>::read_bytes(size_t size) NOEXCEPT
82,873✔
361
{
362
    if (is_zero(size))
82,864✔
363
        return {};
81,695✔
364

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

369
    data_chunk out(size);
1,178✔
370
    do_read_bytes(out.data(), size);
1,178✔
371
    return out;
372
}
373

374
template <typename IStream>
375
chunk_cptr byte_reader<IStream>::read_bytes_cptr(size_t size) NOEXCEPT
10✔
376
{
377
    // This allows caller to read an invalid stream without allocation.
378
    if (!valid())
10✔
379
        return {};
380

381
    const auto cptr = to_allocated<data_chunk>(allocator_, size);
8✔
382
    if (is_zero(size))
8✔
383
        return cptr;
384

385
    if (const auto ptr = const_cast<data_chunk*>(cptr.get()))
4✔
386
        do_read_bytes(ptr->data(), size);
4✔
387
    else
UNCOV
388
        invalidate();
×
389

390
    return cptr;
391
}
392

393
template <typename IStream>
394
void byte_reader<IStream>::read_bytes(uint8_t* buffer, size_t size) NOEXCEPT
21,900✔
395
{
396
    do_read_bytes(buffer, size);
21,900✔
397
}
21,900✔
398

399
// strings
400
// ----------------------------------------------------------------------------
401

402
template <typename IStream>
403
std::string byte_reader<IStream>::read_string(size_t limit) NOEXCEPT
16✔
404
{
405
    return read_string_buffer(read_size(limit));
16✔
406
}
407

408
template <typename IStream>
409
std::string byte_reader<IStream>::read_string_buffer(size_t size) NOEXCEPT
40✔
410
{
411
    // Isolating get_exhausted to first call is an optimization (must clear).
412
    if (get_exhausted())
40✔
413
        return {};
414

415
    // This will produce one (zero) terminating byte if size exceeds available.
416
    std::string out{};
417
    out.reserve(add1(size));
36✔
418
    while (!is_zero(size--) && valid())
236✔
419
        out.push_back(read_byte());
200✔
420

421
    // Removes zero and all after, required for bitcoin string deserialization.
422
    const auto position = out.find('\0');
36✔
423
    out.resize(position == std::string::npos ? out.size() : position);
68✔
424
    ////out.shrink_to_fit();
425

426
    clear();
36✔
427
    return out;
36✔
428
}
429

430
// streams
431
// ----------------------------------------------------------------------------
432

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

442
// context
443
// ----------------------------------------------------------------------------
444

445
template <typename IStream>
446
void byte_reader<IStream>::skip_byte() NOEXCEPT
555✔
447
{
448
    do_skip_bytes(one);
555✔
449
}
555✔
450

451
template <typename IStream>
452
void byte_reader<IStream>::skip_bytes(size_t size) NOEXCEPT
82,578✔
453
{
454
    do_skip_bytes(size);
82,578✔
455
}
82,578✔
456

457
template <typename IStream>
458
void byte_reader<IStream>::skip_variable() NOEXCEPT
24✔
459
{
460
    switch (read_byte())
24✔
461
    {
462
        case varint_eight_bytes:
8✔
463
            do_skip_bytes(8);
8✔
464
            return;
8✔
465
        case varint_four_bytes:
4✔
466
            do_skip_bytes(4);
4✔
467
            return;
4✔
468
        case varint_two_bytes:
4✔
469
            do_skip_bytes(2);
4✔
470
            return;
4✔
471
        default:
472
            return;
473
    }
474
}
475

476
template <typename IStream>
477
void byte_reader<IStream>::rewind_byte() NOEXCEPT
4✔
478
{
479
    do_rewind_bytes(one);
4✔
480
}
4✔
481

482
template <typename IStream>
483
void byte_reader<IStream>::rewind_bytes(size_t size) NOEXCEPT
68✔
484
{
485
    do_rewind_bytes(size);
68✔
486
}
68✔
487

488
template <typename IStream>
489
void byte_reader<IStream>::set_position(size_t absolute) NOEXCEPT
651✔
490
{
491
    // Clear a presumed error state following a read overflow.
492
    clear();
651✔
493

494
    // This allows conversion of and absolute to relative position.
495
    const auto position = get_read_position();
651✔
496

497
    if (absolute == position)
651✔
498
        return;
499

500
    if (absolute > position)
613✔
501
        do_skip_bytes(absolute - position);
6✔
502
    else
503
        do_rewind_bytes(position - absolute);
607✔
504
}
505

506
template <typename IStream>
507
bool byte_reader<IStream>::is_exhausted() const NOEXCEPT
371,804✔
508
{
509
    // True if invalid or if no bytes remain in the stream.
510
    return get_exhausted();
371,804✔
511
}
512

513
// control
514
// ----------------------------------------------------------------------------
515
// These only call non-virtual (private) methods.
516

517
template <typename IStream>
518
size_t byte_reader<IStream>::get_read_position() NOEXCEPT
85,122✔
519
{
520
    return getter();
85,122✔
521
}
522

523
template <typename IStream>
524
void byte_reader<IStream>::set_limit(size_t size) NOEXCEPT
996✔
525
{
526
    limit(size);
527
}
994✔
528

529
template <typename IStream>
530
void byte_reader<IStream>::invalidate() NOEXCEPT
33✔
531
{
532
    // Permanently invalidate the stream/reader.
533
    invalid();
33✔
534
}
17✔
535

536
template <typename IStream>
537
byte_reader<IStream>::operator bool() const NOEXCEPT
84,284✔
538
{
539
    // True if any call created an error state, even if there have been
540
    // subsequent calls, or if any error state preexists on the stream.
541
    return valid();
84,284✔
542
}
543

544
// This should not be necessary with bool() defined, but it is.
545
template <typename IStream>
546
bool byte_reader<IStream>::operator!() const NOEXCEPT
371,253✔
547
{
548
    return !valid();
371,253✔
549
}
550

551
// protected virtual
552
// ----------------------------------------------------------------------------
553
// These may only call non-virtual (private) methods (due to overriding).
554

555
// Suppress istream members may throw inside NOEXCEPT.
556
// The intended behavior in this case is program abort.
557
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
558

559
template <typename IStream>
560
uint8_t byte_reader<IStream>::do_peek_byte() NOEXCEPT
50✔
561
{
562
    if (limiter(one))
50✔
563
        return pad();
564

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

572
    validate();
48✔
573
    return valid() ? value : pad();
48✔
574
}
575

576
template <typename IStream>
577
void byte_reader<IStream>::do_read_bytes(uint8_t* buffer, size_t size) NOEXCEPT
491,566✔
578
{
579
    // Limited reads are not partially filled or padded.
580
    if (limiter(size))
491,566✔
581
        return;
582

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

589
    // Read past stream end invalidates stream unless size exceeds maximum.
590
    BC_ASSERT(size <= maximum);
591
    stream_.read(pointer_cast<char>(buffer),
491,560✔
592
        possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
593

594
    validate();
491,560✔
595
}
596

597
template <typename IStream>
598
void byte_reader<IStream>::do_skip_bytes(size_t size) NOEXCEPT
83,051✔
599
{
600
    if (limiter(size))
83,051✔
601
        return;
602

603
    // Skip past stream end invalidates stream unless size exceeds maximum.
604
    BC_ASSERT(size <= maximum);
605
    seeker(possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
83,045✔
606
}
607

608
template <typename IStream>
609
void byte_reader<IStream>::do_rewind_bytes(size_t size) NOEXCEPT
862✔
610
{
611
    // Given that the stream size is unknown to the reader, the limit may be
612
    // arbitrarily high. This prevents an overflow if sum exceeds max_size_t.
613
    // max_size_t is presumed to exceed the range of IStream::pos_type, in 
614
    // which case this constraint does not affect the limiting behavior.
615
    remaining_ = ceilinged_add(remaining_, size);
1,720✔
616

617
    // Rewind past stream start invalidates stream unless size exceeds maximum.
618
    BC_ASSERT(size <= maximum);
619
    seeker(-possible_narrow_and_sign_cast<typename IStream::pos_type>(size));
862✔
620
}
862✔
621

622
template <typename IStream>
623
bool byte_reader<IStream>::get_exhausted() const NOEXCEPT
195,750✔
624
{
625
    if (is_zero(remaining_))
195,750✔
626
        return true;
627

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

639
    if (!valid())
194,756✔
640
        return true;
641

642
    // Peek to force error on eof, save condition, restore valid stream state.
643
    stream_.peek();
194,740✔
644
    const auto eof = !valid();
194,740✔
645
    stream_.clear();
194,740✔
646

647
    return eof;
194,740✔
648
}
649

650
// private
651
// ----------------------------------------------------------------------------
652
// These may only call other private methods (due to overriding).
653

654
template <typename IStream>
655
bool byte_reader<IStream>::valid() const NOEXCEPT
1,425,434✔
656
{
657
    // zero is the istream documented flag for no error.
658
    return stream_.rdstate() == IStream::goodbit;
1,425,434✔
659
}
660

661
template <typename IStream>
662
void byte_reader<IStream>::invalid() NOEXCEPT
12,290✔
663
{
664
    // If eofbit is set, failbit is generally set on all operations.
665
    // badbit is unrecoverable, set the others to ensure consistency.
666
    stream_.setstate(IStream::eofbit | IStream::failbit | IStream::badbit);
12,290✔
667
}
12,290✔
668

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

680
template <typename IStream>
681
void byte_reader<IStream>::clear() NOEXCEPT
687✔
682
{
683
    // Does not reset the current position.
684
    stream_.clear();
687✔
685
}
687✔
686

687
template <typename IStream>
688
size_t byte_reader<IStream>::getter() NOEXCEPT
85,122✔
689
{
690
    static const auto failure = typename IStream::pos_type(-1);
85,122✔
691
    typename IStream::pos_type position;
692

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

708
    // Max size_t is presumed to exceed max IStream::pos_type.
709
    return position == failure ? zero :
85,122✔
710
        possible_narrow_and_sign_cast<size_t>(position);
85,122✔
711
}
712

713
template <typename IStream>
714
void byte_reader<IStream>::limit(size_t size) NOEXCEPT
996✔
715
{
716
    remaining_ = size;
996✔
717
}
718

719
template <typename IStream>
720
bool byte_reader<IStream>::limiter(size_t size) NOEXCEPT
574,667✔
721
{
722
    if (size > remaining_)
574,667✔
723
    {
724
        // Does not reset the current position or the remaining limit.
725
        invalidate();
14✔
726
        return true;
14✔
727
    }
728

729
    remaining_ -= size;
574,653✔
730
    return false;
574,653✔
731
}
732

733
template <typename IStream>
734
void byte_reader<IStream>::seeker(typename IStream::pos_type offset) NOEXCEPT
83,907✔
735
{
736
    // Force these to be consistent by treating zero seek as a no-op.
737
    // boost/istringstream both succeed on non-empty zero seek.
738
    // istringstream succeeds on empty zero seek, boost sets failbit.
739
    if (is_zero(offset))
83,907✔
740
        return;
741

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

757
BC_POP_WARNING()
758

759
} // namespace system
760
} // namespace libbitcoin
761

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