LCOV - code coverage report
Current view: top level - src - streams.h (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 299 335 89.3 %
Date: 2026-06-25 07:23:51 Functions: 446 810 55.1 %

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

Generated by: LCOV version 1.16