Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
501* Will automatically close the file when it goes out of scope if not null.502* If you need to close the file early, use file.fclose() instead of fclose(file).503*/504class BufferedFile505{506private:507AutoFile& m_src;508uint64_t nSrcPos{0}; //!< how many bytes have been read from source509uint64_t m_read_pos{0}; //!< how many bytes have been read from this510uint64_t nReadLimit; //!< up to which position we're allowed to read511uint64_t nRewind; //!< how many bytes we guarantee to rewind512DataBuffer vchBuf;514//! read data from the source to fill the buffer515bool Fill() {516unsigned int pos = nSrcPos % vchBuf.size();517unsigned int readNow = vchBuf.size() - pos;518unsigned int nAvail = vchBuf.size() - (nSrcPos - m_read_pos) - nRewind;519if (nAvail < readNow)520readNow = nAvail;521if (readNow == 0)522return false;523size_t nBytes{m_src.detail_fread(std::span{vchBuf}.subspan(pos, readNow))};524if (nBytes == 0) {525throw std::ios_base::failure{m_src.feof() ? "BufferedFile::Fill: end of file" : "BufferedFile::Fill: fread failed"};526}527nSrcPos += nBytes;528return true;529}531//! Advance the stream's read pointer (m_read_pos) by up to 'length' bytes,532//! filling the buffer from the file so that at least one byte is available.533//! Return a pointer to the available buffer data and the number of bytes534//! (which may be less than the requested length) that may be accessed535//! beginning at that pointer.536std::pair<std::byte*, size_t> AdvanceStream(size_t length)537{538assert(m_read_pos <= nSrcPos);539if (m_read_pos + length > nReadLimit) {540throw std::ios_base::failure("Attempt to position past buffer limit");541}542// If there are no bytes available, read from the file.543if (m_read_pos == nSrcPos && length > 0) Fill();545size_t buffer_offset{static_cast<size_t>(m_read_pos % vchBuf.size())};546size_t buffer_available{static_cast<size_t>(vchBuf.size() - buffer_offset)};547size_t bytes_until_source_pos{static_cast<size_t>(nSrcPos - m_read_pos)};548size_t advance{std::min({length, buffer_available, bytes_until_source_pos})};549m_read_pos += advance;550return std::make_pair(&vchBuf[buffer_offset], advance);551}553public:554BufferedFile(AutoFile& file LIFETIMEBOUND, uint64_t nBufSize, uint64_t nRewindIn)555: m_src{file}, nReadLimit{std::numeric_limits<uint64_t>::max()}, nRewind{nRewindIn}, vchBuf(nBufSize, std::byte{0})556{557if (nRewindIn >= nBufSize)558throw std::ios_base::failure("Rewind limit must be less than buffer size");559}561//! check whether we're at the end of the source file562bool eof() const {563return m_read_pos == nSrcPos && m_src.feof();564}566//! read a number of bytes567void read(std::span<std::byte> dst)568{569while (dst.size() > 0) {570auto [buffer_pointer, length]{AdvanceStream(dst.size())};571memcpy(dst.data(), buffer_pointer, length);572dst = dst.subspan(length);573}574}576//! Move the read position ahead in the stream to the given position.577//! Use SetPos() to back up in the stream, not SkipTo().578void SkipTo(const uint64_t file_pos)579{580assert(file_pos >= m_read_pos);581while (m_read_pos < file_pos) AdvanceStream(file_pos - m_read_pos);582}584//! return the current reading position585uint64_t GetPos() const {586return m_read_pos;587}589//! rewind to a given reading position590bool SetPos(uint64_t nPos) {591size_t bufsize = vchBuf.size();592if (nPos + bufsize < nSrcPos) {593// rewinding too far, rewind as far as possible594m_read_pos = nSrcPos - bufsize;595return false;596}597if (nPos > nSrcPos) {598// can't go this far forward, go as far as possible599m_read_pos = nSrcPos;600return false;601}602m_read_pos = nPos;603return true;604}606//! prevent reading beyond a certain position607//! no argument removes the limit608bool SetLimit(uint64_t nPos = std::numeric_limits<uint64_t>::max()) {609if (nPos < m_read_pos)610return false;611nReadLimit = nPos;612return true;613}615template<typename T>616BufferedFile& operator>>(T&& obj) {617::Unserialize(*this, obj);618return (*this);619}621//! search for a given byte in the stream, and remain positioned on it622void FindByte(std::byte byte)623{624// For best performance, avoid mod operation within the loop.625size_t buf_offset{size_t(m_read_pos % uint64_t(vchBuf.size()))};626while (true) {627if (m_read_pos == nSrcPos) {628// No more bytes available; read from the file into the buffer,629// setting nSrcPos to one beyond the end of the new data.630// Throws exception if end-of-file reached.631Fill();632}633const size_t len{std::min<size_t>(vchBuf.size() - buf_offset, nSrcPos - m_read_pos)};634const auto it_start{vchBuf.begin() + buf_offset};635const auto it_find{std::find(it_start, it_start + len, byte)};636const size_t inc{size_t(std::distance(it_start, it_find))};637m_read_pos += inc;638if (inc < len) break;639buf_offset += inc;640if (buf_offset >= vchBuf.size()) buf_offset = 0;641}642}643};645/**646* Wrapper that buffers reads from an underlying stream.647* Requires underlying stream to support read() and detail_fread() calls648* to support fixed-size and variable-sized reads, respectively.649*/650template <typename S>651class BufferedReader652{653S& m_src;654DataBuffer m_buf;655size_t m_buf_pos;657public:658//! Requires stream ownership to prevent leaving the stream at an unexpected position after buffered reads.659explicit BufferedReader(S&& stream LIFETIMEBOUND, size_t size = 1 << 16)660requires std::is_rvalue_reference_v<S&&>661: m_src{stream}, m_buf(size), m_buf_pos{size} {}663void read(std::span<std::byte> dst)664{665if (const auto available{std::min(dst.size(), m_buf.size() - m_buf_pos)}) {666std::copy_n(m_buf.begin() + m_buf_pos, available, dst.begin());667m_buf_pos += available;668dst = dst.subspan(available);669}670if (dst.size()) {671assert(m_buf_pos == m_buf.size());672m_src.read(dst);674m_buf_pos = 0;675m_buf.resize(m_src.detail_fread(m_buf));676}677}679template <typename T>680BufferedReader& operator>>(T&& obj)681{682Unserialize(*this, obj);683return *this;684}685};687/**688* Wrapper that buffers writes to an underlying stream.689* Requires underlying stream to support write_buffer() method690* for efficient buffer flushing and obfuscation.691*/692template <typename S>693class BufferedWriter694{695S& m_dst;696DataBuffer m_buf;697size_t m_buf_pos{0};699public:700explicit BufferedWriter(S& stream LIFETIMEBOUND, size_t size = 1 << 16) : m_dst{stream}, m_buf(size) {}702~BufferedWriter() { flush(); }704void flush()705{706if (m_buf_pos) m_dst.write_buffer(std::span{m_buf}.first(m_buf_pos));707m_buf_pos = 0;708}710void write(std::span<const std::byte> src)711{712while (const auto available{std::min(src.size(), m_buf.size() - m_buf_pos)}) {713std::copy_n(src.begin(), available, m_buf.begin() + m_buf_pos);714m_buf_pos += available;715if (m_buf_pos == m_buf.size()) flush();716src = src.subspan(available);717}718}720template <typename T>721BufferedWriter& operator<<(const T& obj)722{723Serialize(*this, obj);724return *this;725}726};728#endif // BITCOIN_STREAMS_H