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

libbitcoin / libbitcoin-system / 9830496261

07 Jul 2024 09:38PM UTC coverage: 82.789% (+0.3%) from 82.53%
9830496261

Pull #1495

github

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

12 of 13 new or added lines in 2 files covered. (92.31%)

2 existing lines in 1 file now uncovered.

9904 of 11963 relevant lines covered (82.79%)

4767514.25 hits per line

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

95.93
/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
    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
    BC_PUSH_WARNING(NO_UNGUARDED_POINTERS)
268
    const auto ptr = const_cast<data_array<Size>*>(cptr.get());
269
    BC_POP_WARNING()
270

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

277
    return cptr;
278
}
279

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

288
    BC_PUSH_WARNING(NO_UNGUARDED_POINTERS)
289
    const auto ptr = const_cast<data_array<Size>*>(cptr.get());
290
    BC_POP_WARNING()
291

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

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

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

308
template <typename IStream>
309
hash_digest byte_reader<IStream>::read_hash() NOEXCEPT
660✔
310
{
311
    return read_forward<hash_size>();
660✔
312
}
313

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

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

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

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

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

344
// byte vectors
345
// ----------------------------------------------------------------------------
346

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

358
    rewind_bytes(size);
40✔
359
    return read_bytes(size);
40✔
360
}
361

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

373
    rewind_bytes(size);
4✔
374
    return read_bytes_cptr(size);
4✔
375
}
376

377
template <typename IStream>
378
data_chunk byte_reader<IStream>::read_bytes(size_t size) NOEXCEPT
82,873✔
379
{
380
    if (is_zero(size))
82,864✔
381
        return {};
81,695✔
382

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

387
    data_chunk out(size);
1,178✔
388
    do_read_bytes(out.data(), size);
1,178✔
389
    return out;
390
}
391

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

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

406
    if (is_zero(size))
8✔
407
        return cptr;
408

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

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

417
    return cptr;
418
}
419

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

426
// strings
427
// ----------------------------------------------------------------------------
428

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

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

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

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

453
    clear();
36✔
454
    return out;
36✔
455
}
456

457
// streams
458
// ----------------------------------------------------------------------------
459

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

469
// context
470
// ----------------------------------------------------------------------------
471

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

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

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

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

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

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

521
    // This allows conversion of and absolute to relative position.
522
    const auto position = get_read_position();
651✔
523

524
    if (absolute == position)
651✔
525
        return;
526

527
    if (absolute > position)
613✔
528
        do_skip_bytes(absolute - position);
6✔
529
    else
530
        do_rewind_bytes(position - absolute);
607✔
531
}
532

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

540
// control
541
// ----------------------------------------------------------------------------
542
// These only call non-virtual (private) methods.
543

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

550
template <typename IStream>
551
void byte_reader<IStream>::set_limit(size_t size) NOEXCEPT
996✔
552
{
553
    limit(size);
554
}
994✔
555

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

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

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

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

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

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

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

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

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

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

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

621
    validate();
491,560✔
622
}
623

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

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

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

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

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

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

666
    if (!valid())
194,756✔
667
        return true;
668

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

674
    return eof;
194,740✔
675
}
676

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

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

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

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

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

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

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

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

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

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

756
    remaining_ -= size;
574,653✔
757
    return false;
574,653✔
758
}
759

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

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

784
BC_POP_WARNING()
785

786
} // namespace system
787
} // namespace libbitcoin
788

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