LCOV - code coverage report
Current view: top level - src - streams.h (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 68 297 22.9 %
Date: 2023-09-26 12:08:55 Functions: 51 465 11.0 %

          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

Generated by: LCOV version 1.14