Line data Source code
1 : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2022 The Bitcoin Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #ifndef BITCOIN_STREAMS_H
7 : #define BITCOIN_STREAMS_H
8 :
9 : #include <serialize.h>
10 : #include <span.h>
11 : #include <support/allocators/zeroafterfree.h>
12 : #include <util/overflow.h>
13 :
14 : #include <algorithm>
15 : #include <assert.h>
16 : #include <cstddef>
17 : #include <cstdio>
18 : #include <ios>
19 : #include <limits>
20 : #include <optional>
21 : #include <stdint.h>
22 : #include <string.h>
23 : #include <string>
24 : #include <utility>
25 : #include <vector>
26 :
27 : namespace util {
28 1 : inline void Xor(Span<std::byte> write, Span<const std::byte> key, size_t key_offset = 0)
29 : {
30 1 : if (key.size() == 0) {
31 0 : return;
32 : }
33 1 : key_offset %= key.size();
34 :
35 10 : for (size_t i = 0, j = key_offset; i != write.size(); i++) {
36 9 : write[i] ^= key[j++];
37 :
38 : // This potentially acts on very many bytes of data, so it's
39 : // important that we calculate `j`, i.e. the `key` index in this
40 : // way instead of doing a %, which would effectively be a division
41 : // for each byte Xor'd -- much slower than need be.
42 9 : if (j == key.size())
43 1 : j = 0;
44 9 : }
45 1 : }
46 : } // namespace util
47 :
48 : template<typename Stream>
49 : class OverrideStream
50 : {
51 : Stream* stream;
52 :
53 : const int nType;
54 : const int nVersion;
55 :
56 : public:
57 0 : OverrideStream(Stream* stream_, int nType_, int nVersion_) : stream(stream_), nType(nType_), nVersion(nVersion_) {}
58 :
59 : template<typename T>
60 0 : OverrideStream<Stream>& operator<<(const T& obj)
61 : {
62 0 : ::Serialize(*this, obj);
63 0 : return (*this);
64 : }
65 :
66 : template<typename T>
67 0 : OverrideStream<Stream>& operator>>(T&& obj)
68 : {
69 0 : ::Unserialize(*this, obj);
70 0 : return (*this);
71 : }
72 :
73 0 : void write(Span<const std::byte> src)
74 : {
75 0 : stream->write(src);
76 0 : }
77 :
78 0 : void read(Span<std::byte> dst)
79 : {
80 0 : stream->read(dst);
81 0 : }
82 :
83 0 : int GetVersion() const { return nVersion; }
84 : int GetType() const { return nType; }
85 0 : size_t size() const { return stream->size(); }
86 : void ignore(size_t size) { return stream->ignore(size); }
87 : };
88 :
89 : /* Minimal stream for overwriting and/or appending to an existing byte vector
90 : *
91 : * The referenced vector will grow as necessary
92 : */
93 : class CVectorWriter
94 : {
95 : public:
96 :
97 : /*
98 : * @param[in] nTypeIn Serialization Type
99 : * @param[in] nVersionIn Serialization Version (including any flags)
100 : * @param[in] vchDataIn Referenced byte vector to overwrite/append
101 : * @param[in] nPosIn Starting position. Vector index where writes should start. The vector will initially
102 : * grow as necessary to max(nPosIn, vec.size()). So to append, use vec.size().
103 : */
104 0 : CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn) : nType(nTypeIn), nVersion(nVersionIn), vchData(vchDataIn), nPos(nPosIn)
105 : {
106 0 : if(nPos > vchData.size())
107 0 : vchData.resize(nPos);
108 0 : }
109 : /*
110 : * (other params same as above)
111 : * @param[in] args A list of items to serialize starting at nPosIn.
112 : */
113 : template <typename... Args>
114 0 : CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn, Args&&... args) : CVectorWriter(nTypeIn, nVersionIn, vchDataIn, nPosIn)
115 : {
116 0 : ::SerializeMany(*this, std::forward<Args>(args)...);
117 0 : }
118 0 : void write(Span<const std::byte> src)
119 : {
120 0 : assert(nPos <= vchData.size());
121 0 : size_t nOverwrite = std::min(src.size(), vchData.size() - nPos);
122 0 : if (nOverwrite) {
123 0 : memcpy(vchData.data() + nPos, src.data(), nOverwrite);
124 0 : }
125 0 : if (nOverwrite < src.size()) {
126 0 : vchData.insert(vchData.end(), UCharCast(src.data()) + nOverwrite, UCharCast(src.end()));
127 0 : }
128 0 : nPos += src.size();
129 0 : }
130 : template<typename T>
131 0 : CVectorWriter& operator<<(const T& obj)
132 : {
133 0 : ::Serialize(*this, obj);
134 0 : return (*this);
135 : }
136 0 : int GetVersion() const
137 : {
138 0 : return nVersion;
139 : }
140 : int GetType() const
141 : {
142 : return nType;
143 : }
144 : private:
145 : const int nType;
146 : const int nVersion;
147 : std::vector<unsigned char>& vchData;
148 : size_t nPos;
149 : };
150 :
151 : /** Minimal stream for reading from an existing byte array by Span.
152 : */
153 : class SpanReader
154 : {
155 : private:
156 : const int m_type;
157 : const int m_version;
158 : Span<const unsigned char> m_data;
159 :
160 : public:
161 :
162 : /**
163 : * @param[in] type Serialization Type
164 : * @param[in] version Serialization Version (including any flags)
165 : * @param[in] data Referenced byte vector to overwrite/append
166 : */
167 0 : SpanReader(int type, int version, Span<const unsigned char> data)
168 0 : : m_type(type), m_version(version), m_data(data) {}
169 :
170 : template<typename T>
171 0 : SpanReader& operator>>(T&& obj)
172 : {
173 0 : ::Unserialize(*this, obj);
174 0 : return (*this);
175 : }
176 :
177 0 : int GetVersion() const { return m_version; }
178 : int GetType() const { return m_type; }
179 :
180 : size_t size() const { return m_data.size(); }
181 0 : bool empty() const { return m_data.empty(); }
182 :
183 0 : void read(Span<std::byte> dst)
184 : {
185 0 : if (dst.size() == 0) {
186 0 : return;
187 : }
188 :
189 : // Read from the beginning of the buffer
190 0 : if (dst.size() > m_data.size()) {
191 0 : throw std::ios_base::failure("SpanReader::read(): end of data");
192 : }
193 0 : memcpy(dst.data(), m_data.data(), dst.size());
194 0 : m_data = m_data.subspan(dst.size());
195 0 : }
196 : };
197 :
198 : /** Double ended buffer combining vector and stream-like interfaces.
199 : *
200 : * >> and << read and write unformatted data using the above serialization templates.
201 : * Fills with data in linear time; some stringstream implementations take N^2 time.
202 : */
203 : class DataStream
204 : {
205 : protected:
206 : using vector_type = SerializeData;
207 : vector_type vch;
208 56546 : vector_type::size_type m_read_pos{0};
209 :
210 : public:
211 : typedef vector_type::allocator_type allocator_type;
212 : typedef vector_type::size_type size_type;
213 : typedef vector_type::difference_type difference_type;
214 : typedef vector_type::reference reference;
215 : typedef vector_type::const_reference const_reference;
216 : typedef vector_type::value_type value_type;
217 : typedef vector_type::iterator iterator;
218 : typedef vector_type::const_iterator const_iterator;
219 : typedef vector_type::reverse_iterator reverse_iterator;
220 :
221 38544 : explicit DataStream() {}
222 0 : explicit DataStream(Span<const uint8_t> sp) : DataStream{AsBytes(sp)} {}
223 74548 : explicit DataStream(Span<const value_type> sp) : vch(sp.data(), sp.data() + sp.size()) {}
224 :
225 0 : std::string str() const
226 : {
227 0 : return std::string{UCharCast(data()), UCharCast(data() + size())};
228 0 : }
229 :
230 :
231 : //
232 : // Vector subset
233 : //
234 : const_iterator begin() const { return vch.begin() + m_read_pos; }
235 0 : iterator begin() { return vch.begin() + m_read_pos; }
236 : const_iterator end() const { return vch.end(); }
237 0 : iterator end() { return vch.end(); }
238 19273 : size_type size() const { return vch.size() - m_read_pos; }
239 0 : bool empty() const { return vch.size() == m_read_pos; }
240 0 : void resize(size_type n, value_type c = value_type{}) { vch.resize(n + m_read_pos, c); }
241 19272 : void reserve(size_type n) { vch.reserve(n + m_read_pos); }
242 : const_reference operator[](size_type pos) const { return vch[pos + m_read_pos]; }
243 0 : reference operator[](size_type pos) { return vch[pos + m_read_pos]; }
244 2 : void clear() { vch.clear(); m_read_pos = 0; }
245 19273 : value_type* data() { return vch.data() + m_read_pos; }
246 0 : const value_type* data() const { return vch.data() + m_read_pos; }
247 :
248 : inline void Compact()
249 : {
250 : vch.erase(vch.begin(), vch.begin() + m_read_pos);
251 : m_read_pos = 0;
252 : }
253 :
254 : bool Rewind(std::optional<size_type> n = std::nullopt)
255 : {
256 : // Total rewind if no size is passed
257 : if (!n) {
258 : m_read_pos = 0;
259 : return true;
260 : }
261 : // Rewind by n characters if the buffer hasn't been compacted yet
262 : if (*n > m_read_pos)
263 : return false;
264 : m_read_pos -= *n;
265 : return true;
266 : }
267 :
268 :
269 : //
270 : // Stream subset
271 : //
272 0 : bool eof() const { return size() == 0; }
273 0 : int in_avail() const { return size(); }
274 :
275 160402 : void read(Span<value_type> dst)
276 : {
277 160402 : if (dst.size() == 0) return;
278 :
279 : // Read from the beginning of the buffer
280 160402 : auto next_read_pos{CheckedAdd(m_read_pos, dst.size())};
281 160402 : if (!next_read_pos.has_value() || next_read_pos.value() > vch.size()) {
282 10159 : throw std::ios_base::failure("DataStream::read(): end of data");
283 : }
284 150243 : memcpy(dst.data(), &vch[m_read_pos], dst.size());
285 150243 : if (next_read_pos.value() == vch.size()) {
286 14300 : m_read_pos = 0;
287 14300 : vch.clear();
288 14300 : return;
289 : }
290 135943 : m_read_pos = next_read_pos.value();
291 150243 : }
292 :
293 0 : void ignore(size_t num_ignore)
294 : {
295 : // Ignore from the beginning of the buffer
296 0 : auto next_read_pos{CheckedAdd(m_read_pos, num_ignore)};
297 0 : if (!next_read_pos.has_value() || next_read_pos.value() > vch.size()) {
298 0 : throw std::ios_base::failure("DataStream::ignore(): end of data");
299 : }
300 0 : if (next_read_pos.value() == vch.size()) {
301 0 : m_read_pos = 0;
302 0 : vch.clear();
303 0 : return;
304 : }
305 0 : m_read_pos = next_read_pos.value();
306 0 : }
307 :
308 57935 : void write(Span<const value_type> src)
309 : {
310 : // Write to the end of the buffer
311 57935 : vch.insert(vch.end(), src.begin(), src.end());
312 57935 : }
313 :
314 : template<typename T>
315 38530 : DataStream& operator<<(const T& obj)
316 : {
317 38530 : ::Serialize(*this, obj);
318 38530 : return (*this);
319 : }
320 :
321 : template<typename T>
322 0 : DataStream& operator>>(T&& obj)
323 : {
324 0 : ::Unserialize(*this, obj);
325 0 : return (*this);
326 : }
327 :
328 : /**
329 : * XOR the contents of this stream with a certain key.
330 : *
331 : * @param[in] key The key used to XOR the data in this stream.
332 : */
333 1 : void Xor(const std::vector<unsigned char>& key)
334 : {
335 1 : util::Xor(MakeWritableByteSpan(*this), MakeByteSpan(key));
336 1 : }
337 : };
338 :
339 : class CDataStream : public DataStream
340 : {
341 : private:
342 : int nType;
343 : int nVersion;
344 :
345 : public:
346 0 : explicit CDataStream(int nTypeIn, int nVersionIn)
347 0 : : nType{nTypeIn},
348 0 : nVersion{nVersionIn} {}
349 :
350 35221 : explicit CDataStream(Span<const uint8_t> sp, int type, int version) : CDataStream{AsBytes(sp), type, version} {}
351 35265 : explicit CDataStream(Span<const value_type> sp, int nTypeIn, int nVersionIn)
352 35265 : : DataStream{sp},
353 35265 : nType{nTypeIn},
354 35265 : nVersion{nVersionIn} {}
355 :
356 0 : int GetType() const { return nType; }
357 0 : void SetVersion(int n) { nVersion = n; }
358 0 : int GetVersion() const { return nVersion; }
359 :
360 : template <typename T>
361 0 : CDataStream& operator<<(const T& obj)
362 : {
363 0 : ::Serialize(*this, obj);
364 0 : return *this;
365 : }
366 :
367 : template <typename T>
368 18183 : CDataStream& operator>>(T&& obj)
369 : {
370 18183 : ::Unserialize(*this, obj);
371 18183 : return *this;
372 : }
373 : };
374 :
375 : template <typename IStream>
376 : class BitStreamReader
377 : {
378 : private:
379 : IStream& m_istream;
380 :
381 : /// Buffered byte read in from the input stream. A new byte is read into the
382 : /// buffer when m_offset reaches 8.
383 0 : uint8_t m_buffer{0};
384 :
385 : /// Number of high order bits in m_buffer already returned by previous
386 : /// Read() calls. The next bit to be returned is at this offset from the
387 : /// most significant bit position.
388 0 : int m_offset{8};
389 :
390 : public:
391 0 : explicit BitStreamReader(IStream& istream) : m_istream(istream) {}
392 :
393 : /** Read the specified number of bits from the stream. The data is returned
394 : * in the nbits least significant bits of a 64-bit uint.
395 : */
396 0 : uint64_t Read(int nbits) {
397 0 : if (nbits < 0 || nbits > 64) {
398 0 : throw std::out_of_range("nbits must be between 0 and 64");
399 : }
400 :
401 0 : uint64_t data = 0;
402 0 : while (nbits > 0) {
403 0 : if (m_offset == 8) {
404 0 : m_istream >> m_buffer;
405 0 : m_offset = 0;
406 0 : }
407 :
408 0 : int bits = std::min(8 - m_offset, nbits);
409 0 : data <<= bits;
410 0 : data |= static_cast<uint8_t>(m_buffer << m_offset) >> (8 - bits);
411 0 : m_offset += bits;
412 0 : nbits -= bits;
413 : }
414 0 : return data;
415 0 : }
416 : };
417 :
418 : template <typename OStream>
419 : class BitStreamWriter
420 : {
421 : private:
422 : OStream& m_ostream;
423 :
424 : /// Buffered byte waiting to be written to the output stream. The byte is
425 : /// written buffer when m_offset reaches 8 or Flush() is called.
426 0 : uint8_t m_buffer{0};
427 :
428 : /// Number of high order bits in m_buffer already written by previous
429 : /// Write() calls and not yet flushed to the stream. The next bit to be
430 : /// written to is at this offset from the most significant bit position.
431 0 : int m_offset{0};
432 :
433 : public:
434 0 : explicit BitStreamWriter(OStream& ostream) : m_ostream(ostream) {}
435 :
436 0 : ~BitStreamWriter()
437 : {
438 0 : Flush();
439 0 : }
440 :
441 : /** Write the nbits least significant bits of a 64-bit int to the output
442 : * stream. Data is buffered until it completes an octet.
443 : */
444 0 : void Write(uint64_t data, int nbits) {
445 0 : if (nbits < 0 || nbits > 64) {
446 0 : throw std::out_of_range("nbits must be between 0 and 64");
447 : }
448 :
449 0 : while (nbits > 0) {
450 0 : int bits = std::min(8 - m_offset, nbits);
451 0 : m_buffer |= (data << (64 - nbits)) >> (64 - 8 + m_offset);
452 0 : m_offset += bits;
453 0 : nbits -= bits;
454 :
455 0 : if (m_offset == 8) {
456 0 : Flush();
457 0 : }
458 : }
459 0 : }
460 :
461 : /** Flush any unwritten bits to the output stream, padding with 0's to the
462 : * next byte boundary.
463 : */
464 0 : void Flush() {
465 0 : if (m_offset == 0) {
466 0 : return;
467 : }
468 :
469 0 : m_ostream << m_buffer;
470 0 : m_buffer = 0;
471 0 : m_offset = 0;
472 0 : }
473 : };
474 :
475 : /** Non-refcounted RAII wrapper for FILE*
476 : *
477 : * Will automatically close the file when it goes out of scope if not null.
478 : * If you're returning the file pointer, return file.release().
479 : * If you need to close the file early, use file.fclose() instead of fclose(file).
480 : */
481 : class AutoFile
482 : {
483 : protected:
484 : std::FILE* m_file;
485 : const std::vector<std::byte> m_xor;
486 :
487 : public:
488 403 : explicit AutoFile(std::FILE* file, std::vector<std::byte> data_xor={}) : m_file{file}, m_xor{std::move(data_xor)} {}
489 :
490 403 : ~AutoFile() { fclose(); }
491 :
492 : // Disallow copies
493 : AutoFile(const AutoFile&) = delete;
494 : AutoFile& operator=(const AutoFile&) = delete;
495 :
496 0 : bool feof() const { return std::feof(m_file); }
497 :
498 403 : int fclose()
499 : {
500 403 : if (auto rel{release()}) return std::fclose(rel);
501 1 : return 0;
502 403 : }
503 :
504 : /** Get wrapped FILE* with transfer of ownership.
505 : * @note This will invalidate the AutoFile object, and makes it the responsibility of the caller
506 : * of this function to clean up the returned FILE*.
507 : */
508 403 : std::FILE* release()
509 : {
510 403 : std::FILE* ret{m_file};
511 403 : m_file = nullptr;
512 403 : return ret;
513 : }
514 :
515 : /** Get wrapped FILE* without transfer of ownership.
516 : * @note Ownership of the FILE* will remain with this class. Use this only if the scope of the
517 : * AutoFile outlives use of the passed pointer.
518 : */
519 401 : std::FILE* Get() const { return m_file; }
520 :
521 : /** Return true if the wrapped FILE* is nullptr, false otherwise.
522 : */
523 403 : bool IsNull() const { return m_file == nullptr; }
524 :
525 : /** Implementation detail, only used internally. */
526 : std::size_t detail_fread(Span<std::byte> dst);
527 :
528 : //
529 : // Stream subset
530 : //
531 : void read(Span<std::byte> dst);
532 : void ignore(size_t nSize);
533 : void write(Span<const std::byte> src);
534 :
535 : template <typename T>
536 1000 : AutoFile& operator<<(const T& obj)
537 : {
538 1000 : ::Serialize(*this, obj);
539 1000 : return *this;
540 : }
541 :
542 : template <typename T>
543 0 : AutoFile& operator>>(T&& obj)
544 : {
545 0 : ::Unserialize(*this, obj);
546 0 : return *this;
547 : }
548 : };
549 :
550 : class CAutoFile : public AutoFile
551 : {
552 : private:
553 : const int nVersion;
554 :
555 : public:
556 1233 : explicit CAutoFile(std::FILE* file, int version, std::vector<std::byte> data_xor = {}) : AutoFile{file, std::move(data_xor)}, nVersion{version} {}
557 2449 : int GetVersion() const { return nVersion; }
558 :
559 : template<typename T>
560 13730 : CAutoFile& operator<<(const T& obj)
561 : {
562 13730 : ::Serialize(*this, obj);
563 13730 : return (*this);
564 : }
565 :
566 : template<typename T>
567 69 : CAutoFile& operator>>(T&& obj)
568 : {
569 69 : ::Unserialize(*this, obj);
570 69 : return (*this);
571 : }
572 : };
573 :
574 : /** Non-refcounted RAII wrapper around a FILE* that implements a ring buffer to
575 : * deserialize from. It guarantees the ability to rewind a given number of bytes.
576 : *
577 : * Will automatically close the file when it goes out of scope if not null.
578 : * If you need to close the file early, use file.fclose() instead of fclose(file).
579 : */
580 : class BufferedFile
581 : {
582 : private:
583 : const int nVersion;
584 :
585 : FILE *src; //!< source file
586 0 : uint64_t nSrcPos{0}; //!< how many bytes have been read from source
587 0 : uint64_t m_read_pos{0}; //!< how many bytes have been read from this
588 : uint64_t nReadLimit; //!< up to which position we're allowed to read
589 : uint64_t nRewind; //!< how many bytes we guarantee to rewind
590 : std::vector<std::byte> vchBuf; //!< the buffer
591 :
592 : //! read data from the source to fill the buffer
593 0 : bool Fill() {
594 0 : unsigned int pos = nSrcPos % vchBuf.size();
595 0 : unsigned int readNow = vchBuf.size() - pos;
596 0 : unsigned int nAvail = vchBuf.size() - (nSrcPos - m_read_pos) - nRewind;
597 0 : if (nAvail < readNow)
598 0 : readNow = nAvail;
599 0 : if (readNow == 0)
600 0 : return false;
601 0 : size_t nBytes = fread((void*)&vchBuf[pos], 1, readNow, src);
602 0 : if (nBytes == 0) {
603 0 : throw std::ios_base::failure(feof(src) ? "BufferedFile::Fill: end of file" : "BufferedFile::Fill: fread failed");
604 : }
605 0 : nSrcPos += nBytes;
606 0 : return true;
607 0 : }
608 :
609 : //! Advance the stream's read pointer (m_read_pos) by up to 'length' bytes,
610 : //! filling the buffer from the file so that at least one byte is available.
611 : //! Return a pointer to the available buffer data and the number of bytes
612 : //! (which may be less than the requested length) that may be accessed
613 : //! beginning at that pointer.
614 0 : std::pair<std::byte*, size_t> AdvanceStream(size_t length)
615 : {
616 0 : assert(m_read_pos <= nSrcPos);
617 0 : if (m_read_pos + length > nReadLimit) {
618 0 : throw std::ios_base::failure("Attempt to position past buffer limit");
619 : }
620 : // If there are no bytes available, read from the file.
621 0 : if (m_read_pos == nSrcPos && length > 0) Fill();
622 :
623 0 : size_t buffer_offset{static_cast<size_t>(m_read_pos % vchBuf.size())};
624 0 : size_t buffer_available{static_cast<size_t>(vchBuf.size() - buffer_offset)};
625 0 : size_t bytes_until_source_pos{static_cast<size_t>(nSrcPos - m_read_pos)};
626 0 : size_t advance{std::min({length, buffer_available, bytes_until_source_pos})};
627 0 : m_read_pos += advance;
628 0 : return std::make_pair(&vchBuf[buffer_offset], advance);
629 0 : }
630 :
631 : public:
632 0 : BufferedFile(FILE* fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nVersionIn)
633 0 : : nVersion{nVersionIn}, nReadLimit{std::numeric_limits<uint64_t>::max()}, nRewind{nRewindIn}, vchBuf(nBufSize, std::byte{0})
634 : {
635 0 : if (nRewindIn >= nBufSize)
636 0 : throw std::ios_base::failure("Rewind limit must be less than buffer size");
637 0 : src = fileIn;
638 0 : }
639 :
640 0 : ~BufferedFile()
641 : {
642 0 : fclose();
643 0 : }
644 :
645 : // Disallow copies
646 : BufferedFile(const BufferedFile&) = delete;
647 : BufferedFile& operator=(const BufferedFile&) = delete;
648 :
649 0 : int GetVersion() const { return nVersion; }
650 :
651 0 : void fclose()
652 : {
653 0 : if (src) {
654 0 : ::fclose(src);
655 0 : src = nullptr;
656 0 : }
657 0 : }
658 :
659 : //! check whether we're at the end of the source file
660 0 : bool eof() const {
661 0 : return m_read_pos == nSrcPos && feof(src);
662 : }
663 :
664 : //! read a number of bytes
665 0 : void read(Span<std::byte> dst)
666 : {
667 0 : while (dst.size() > 0) {
668 0 : auto [buffer_pointer, length]{AdvanceStream(dst.size())};
669 0 : memcpy(dst.data(), buffer_pointer, length);
670 0 : dst = dst.subspan(length);
671 : }
672 0 : }
673 :
674 : //! Move the read position ahead in the stream to the given position.
675 : //! Use SetPos() to back up in the stream, not SkipTo().
676 0 : void SkipTo(const uint64_t file_pos)
677 : {
678 0 : assert(file_pos >= m_read_pos);
679 0 : while (m_read_pos < file_pos) AdvanceStream(file_pos - m_read_pos);
680 0 : }
681 :
682 : //! return the current reading position
683 0 : uint64_t GetPos() const {
684 0 : return m_read_pos;
685 : }
686 :
687 : //! rewind to a given reading position
688 0 : bool SetPos(uint64_t nPos) {
689 0 : size_t bufsize = vchBuf.size();
690 0 : if (nPos + bufsize < nSrcPos) {
691 : // rewinding too far, rewind as far as possible
692 0 : m_read_pos = nSrcPos - bufsize;
693 0 : return false;
694 : }
695 0 : if (nPos > nSrcPos) {
696 : // can't go this far forward, go as far as possible
697 0 : m_read_pos = nSrcPos;
698 0 : return false;
699 : }
700 0 : m_read_pos = nPos;
701 0 : return true;
702 0 : }
703 :
704 : //! prevent reading beyond a certain position
705 : //! no argument removes the limit
706 0 : bool SetLimit(uint64_t nPos = std::numeric_limits<uint64_t>::max()) {
707 0 : if (nPos < m_read_pos)
708 0 : return false;
709 0 : nReadLimit = nPos;
710 0 : return true;
711 0 : }
712 :
713 : template<typename T>
714 0 : BufferedFile& operator>>(T&& obj) {
715 0 : ::Unserialize(*this, obj);
716 0 : return (*this);
717 : }
718 :
719 : //! search for a given byte in the stream, and remain positioned on it
720 0 : void FindByte(std::byte byte)
721 : {
722 : // For best performance, avoid mod operation within the loop.
723 0 : size_t buf_offset{size_t(m_read_pos % uint64_t(vchBuf.size()))};
724 0 : while (true) {
725 0 : if (m_read_pos == nSrcPos) {
726 : // No more bytes available; read from the file into the buffer,
727 : // setting nSrcPos to one beyond the end of the new data.
728 : // Throws exception if end-of-file reached.
729 0 : Fill();
730 0 : }
731 0 : const size_t len{std::min<size_t>(vchBuf.size() - buf_offset, nSrcPos - m_read_pos)};
732 0 : const auto it_start{vchBuf.begin() + buf_offset};
733 0 : const auto it_find{std::find(it_start, it_start + len, byte)};
734 0 : const size_t inc{size_t(std::distance(it_start, it_find))};
735 0 : m_read_pos += inc;
736 0 : if (inc < len) break;
737 0 : buf_offset += inc;
738 0 : if (buf_offset >= vchBuf.size()) buf_offset = 0;
739 : }
740 0 : }
741 : };
742 :
743 : #endif // BITCOIN_STREAMS_H
|