Branch data 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 <ios>
# 17 : : #include <limits>
# 18 : : #include <optional>
# 19 : : #include <stdint.h>
# 20 : : #include <stdio.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 : 68246 : OverrideStream(Stream* stream_, int nType_, int nVersion_) : stream(stream_), nType(nType_), nVersion(nVersion_) {}
# 36 : :
# 37 : : template<typename T>
# 38 : : OverrideStream<Stream>& operator<<(const T& obj)
# 39 : 2668163 : {
# 40 : : // Serialize to this stream
# 41 : 2668163 : ::Serialize(*this, obj);
# 42 : 2668163 : return (*this);
# 43 : 2668163 : }
# 44 : :
# 45 : : template<typename T>
# 46 : : OverrideStream<Stream>& operator>>(T&& obj)
# 47 : 396272 : {
# 48 : : // Unserialize from this stream
# 49 : 396272 : ::Unserialize(*this, obj);
# 50 : 396272 : return (*this);
# 51 : 396272 : }
# 52 : :
# 53 : : void write(Span<const std::byte> src)
# 54 : 3214508 : {
# 55 : 3214508 : stream->write(src);
# 56 : 3214508 : }
# 57 : :
# 58 : : void read(Span<std::byte> dst)
# 59 : 483629 : {
# 60 : 483629 : stream->read(dst);
# 61 : 483629 : }
# 62 : :
# 63 : 220763 : int GetVersion() const { return nVersion; }
# 64 : 174493 : int GetType() const { return nType; }
# 65 : 2800 : 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 : : CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn) : nType(nTypeIn), nVersion(nVersionIn), vchData(vchDataIn), nPos(nPosIn)
# 85 : 230851 : {
# 86 [ + + ]: 230851 : if(nPos > vchData.size())
# 87 : 2 : vchData.resize(nPos);
# 88 : 230851 : }
# 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 : : CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn, Args&&... args) : CVectorWriter(nTypeIn, nVersionIn, vchDataIn, nPosIn)
# 95 : 219523 : {
# 96 : 219523 : ::SerializeMany(*this, std::forward<Args>(args)...);
# 97 : 219523 : }
# 98 : : void write(Span<const std::byte> src)
# 99 : 10810978 : {
# 100 : 10810978 : assert(nPos <= vchData.size());
# 101 : 0 : size_t nOverwrite = std::min(src.size(), vchData.size() - nPos);
# 102 [ + + ]: 10810978 : if (nOverwrite) {
# 103 : 38 : memcpy(vchData.data() + nPos, src.data(), nOverwrite);
# 104 : 38 : }
# 105 [ + + ]: 10810978 : if (nOverwrite < src.size()) {
# 106 : 10810932 : vchData.insert(vchData.end(), UCharCast(src.data()) + nOverwrite, UCharCast(src.end()));
# 107 : 10810932 : }
# 108 : 10810978 : nPos += src.size();
# 109 : 10810978 : }
# 110 : : template<typename T>
# 111 : : CVectorWriter& operator<<(const T& obj)
# 112 : 345196 : {
# 113 : : // Serialize to this stream
# 114 : 345196 : ::Serialize(*this, obj);
# 115 : 345196 : return (*this);
# 116 : 345196 : }
# 117 : : int GetVersion() const
# 118 : 66140 : {
# 119 : 66140 : return nVersion;
# 120 : 66140 : }
# 121 : : int GetType() const
# 122 : 28715 : {
# 123 : 28715 : return nType;
# 124 : 28715 : }
# 125 : : private:
# 126 : : const int nType;
# 127 : : const int nVersion;
# 128 : : std::vector<unsigned char>& vchData;
# 129 : : size_t nPos;
# 130 : : };
# 131 : :
# 132 : : /** Minimal stream for reading from an existing byte array by Span.
# 133 : : */
# 134 : : class SpanReader
# 135 : : {
# 136 : : private:
# 137 : : const int m_type;
# 138 : : const int m_version;
# 139 : : Span<const unsigned char> m_data;
# 140 : :
# 141 : : public:
# 142 : :
# 143 : : /**
# 144 : : * @param[in] type Serialization Type
# 145 : : * @param[in] version Serialization Version (including any flags)
# 146 : : * @param[in] data Referenced byte vector to overwrite/append
# 147 : : */
# 148 : : SpanReader(int type, int version, Span<const unsigned char> data)
# 149 : 5206 : : m_type(type), m_version(version), m_data(data) {}
# 150 : :
# 151 : : template<typename T>
# 152 : : SpanReader& operator>>(T&& obj)
# 153 : 28596 : {
# 154 : : // Unserialize from this stream
# 155 : 28596 : ::Unserialize(*this, obj);
# 156 : 28596 : return (*this);
# 157 : 28596 : }
# 158 : :
# 159 : 2 : int GetVersion() const { return m_version; }
# 160 : 0 : int GetType() const { return m_type; }
# 161 : :
# 162 : 10 : size_t size() const { return m_data.size(); }
# 163 : 724 : bool empty() const { return m_data.empty(); }
# 164 : :
# 165 : : void read(Span<std::byte> dst)
# 166 : 33908 : {
# 167 [ - + ]: 33908 : if (dst.size() == 0) {
# 168 : 0 : return;
# 169 : 0 : }
# 170 : :
# 171 : : // Read from the beginning of the buffer
# 172 [ + + ]: 33908 : if (dst.size() > m_data.size()) {
# 173 : 6 : throw std::ios_base::failure("SpanReader::read(): end of data");
# 174 : 6 : }
# 175 : 33902 : memcpy(dst.data(), m_data.data(), dst.size());
# 176 : 33902 : m_data = m_data.subspan(dst.size());
# 177 : 33902 : }
# 178 : : };
# 179 : :
# 180 : : /** Double ended buffer combining vector and stream-like interfaces.
# 181 : : *
# 182 : : * >> and << read and write unformatted data using the above serialization templates.
# 183 : : * Fills with data in linear time; some stringstream implementations take N^2 time.
# 184 : : */
# 185 : : class CDataStream
# 186 : : {
# 187 : : protected:
# 188 : : using vector_type = SerializeData;
# 189 : : vector_type vch;
# 190 : : vector_type::size_type m_read_pos{0};
# 191 : :
# 192 : : int nType;
# 193 : : int nVersion;
# 194 : :
# 195 : : public:
# 196 : : typedef vector_type::allocator_type allocator_type;
# 197 : : typedef vector_type::size_type size_type;
# 198 : : typedef vector_type::difference_type difference_type;
# 199 : : typedef vector_type::reference reference;
# 200 : : typedef vector_type::const_reference const_reference;
# 201 : : typedef vector_type::value_type value_type;
# 202 : : typedef vector_type::iterator iterator;
# 203 : : typedef vector_type::const_iterator const_iterator;
# 204 : : typedef vector_type::reverse_iterator reverse_iterator;
# 205 : :
# 206 : : explicit CDataStream(int nTypeIn, int nVersionIn)
# 207 : : : nType{nTypeIn},
# 208 : 12141672 : nVersion{nVersionIn} {}
# 209 : :
# 210 : 26743 : explicit CDataStream(Span<const uint8_t> sp, int type, int version) : CDataStream{AsBytes(sp), type, version} {}
# 211 : : explicit CDataStream(Span<const value_type> sp, int nTypeIn, int nVersionIn)
# 212 : : : vch(sp.data(), sp.data() + sp.size()),
# 213 : : nType{nTypeIn},
# 214 : 391109 : nVersion{nVersionIn} {}
# 215 : :
# 216 : : template <typename... Args>
# 217 : : CDataStream(int nTypeIn, int nVersionIn, Args&&... args)
# 218 : : : nType{nTypeIn},
# 219 : : nVersion{nVersionIn}
# 220 : 2 : {
# 221 : 2 : ::SerializeMany(*this, std::forward<Args>(args)...);
# 222 : 2 : }
# 223 : :
# 224 : : std::string str() const
# 225 : 557 : {
# 226 : 557 : return std::string{UCharCast(data()), UCharCast(data() + size())};
# 227 : 557 : }
# 228 : :
# 229 : :
# 230 : : //
# 231 : : // Vector subset
# 232 : : //
# 233 : 0 : const_iterator begin() const { return vch.begin() + m_read_pos; }
# 234 : 141064 : iterator begin() { return vch.begin() + m_read_pos; }
# 235 : 0 : const_iterator end() const { return vch.end(); }
# 236 : 70532 : iterator end() { return vch.end(); }
# 237 : 60422116 : size_type size() const { return vch.size() - m_read_pos; }
# 238 : 34975 : bool empty() const { return vch.size() == m_read_pos; }
# 239 : 224601 : void resize(size_type n, value_type c = value_type{}) { vch.resize(n + m_read_pos, c); }
# 240 : 11468595 : void reserve(size_type n) { vch.reserve(n + m_read_pos); }
# 241 : 0 : const_reference operator[](size_type pos) const { return vch[pos + m_read_pos]; }
# 242 : 35293329 : reference operator[](size_type pos) { return vch[pos + m_read_pos]; }
# 243 : 1011610 : void clear() { vch.clear(); m_read_pos = 0; }
# 244 : 11482046 : value_type* data() { return vch.data() + m_read_pos; }
# 245 : 2634 : const value_type* data() const { return vch.data() + m_read_pos; }
# 246 : :
# 247 : : inline void Compact()
# 248 : 0 : {
# 249 : 0 : vch.erase(vch.begin(), vch.begin() + m_read_pos);
# 250 : 0 : m_read_pos = 0;
# 251 : 0 : }
# 252 : :
# 253 : : bool Rewind(std::optional<size_type> n = std::nullopt)
# 254 : 0 : {
# 255 : 0 : // Total rewind if no size is passed
# 256 : 0 : if (!n) {
# 257 : 0 : m_read_pos = 0;
# 258 : 0 : return true;
# 259 : 0 : }
# 260 : 0 : // Rewind by n characters if the buffer hasn't been compacted yet
# 261 : 0 : if (*n > m_read_pos)
# 262 : 0 : return false;
# 263 : 0 : m_read_pos -= *n;
# 264 : 0 : return true;
# 265 : 0 : }
# 266 : :
# 267 : :
# 268 : : //
# 269 : : // Stream subset
# 270 : : //
# 271 : 153 : bool eof() const { return size() == 0; }
# 272 : 0 : CDataStream* rdbuf() { return this; }
# 273 : 1267 : int in_avail() const { return size(); }
# 274 : :
# 275 : 97690 : void SetType(int n) { nType = n; }
# 276 : 194940 : int GetType() const { return nType; }
# 277 : 107424 : void SetVersion(int n) { nVersion = n; }
# 278 : 355482 : int GetVersion() const { return nVersion; }
# 279 : :
# 280 : : void read(Span<value_type> dst)
# 281 : 11461627 : {
# 282 [ + + ]: 11461627 : if (dst.size() == 0) return;
# 283 : :
# 284 : : // Read from the beginning of the buffer
# 285 : 11459263 : auto next_read_pos{CheckedAdd(m_read_pos, dst.size())};
# 286 [ + + ][ + + ]: 11459267 : if (!next_read_pos.has_value() || next_read_pos.value() > vch.size()) {
# 287 : 2054 : throw std::ios_base::failure("CDataStream::read(): end of data");
# 288 : 2054 : }
# 289 : 11457209 : memcpy(dst.data(), &vch[m_read_pos], dst.size());
# 290 [ + + ]: 11457209 : if (next_read_pos.value() == vch.size()) {
# 291 : 749774 : m_read_pos = 0;
# 292 : 749774 : vch.clear();
# 293 : 749774 : return;
# 294 : 749774 : }
# 295 : 10707435 : m_read_pos = next_read_pos.value();
# 296 : 10707435 : }
# 297 : :
# 298 : : void ignore(size_t num_ignore)
# 299 : 2168 : {
# 300 : : // Ignore from the beginning of the buffer
# 301 : 2168 : auto next_read_pos{CheckedAdd(m_read_pos, num_ignore)};
# 302 [ - + ][ + + ]: 2168 : if (!next_read_pos.has_value() || next_read_pos.value() > vch.size()) {
# 303 : 2 : throw std::ios_base::failure("CDataStream::ignore(): end of data");
# 304 : 2 : }
# 305 [ + + ]: 2166 : if (next_read_pos.value() == vch.size()) {
# 306 : 6 : m_read_pos = 0;
# 307 : 6 : vch.clear();
# 308 : 6 : return;
# 309 : 6 : }
# 310 : 2160 : m_read_pos = next_read_pos.value();
# 311 : 2160 : }
# 312 : :
# 313 : : void write(Span<const value_type> src)
# 314 : 66772987 : {
# 315 : : // Write to the end of the buffer
# 316 : 66772987 : vch.insert(vch.end(), src.begin(), src.end());
# 317 : 66772987 : }
# 318 : :
# 319 : : template<typename Stream>
# 320 : : void Serialize(Stream& s) const
# 321 : 0 : {
# 322 : : // Special case: stream << stream concatenates like stream += stream
# 323 [ # # ]: 0 : if (!vch.empty())
# 324 : 0 : s.write(MakeByteSpan(vch));
# 325 : 0 : }
# 326 : :
# 327 : : template<typename T>
# 328 : : CDataStream& operator<<(const T& obj)
# 329 : 14389813 : {
# 330 : : // Serialize to this stream
# 331 : 14389813 : ::Serialize(*this, obj);
# 332 : 14389813 : return (*this);
# 333 : 14389813 : }
# 334 : :
# 335 : : template<typename T>
# 336 : : CDataStream& operator>>(T&& obj)
# 337 : 2577935 : {
# 338 : : // Unserialize from this stream
# 339 : 2577935 : ::Unserialize(*this, obj);
# 340 : 2577935 : return (*this);
# 341 : 2577935 : }
# 342 : :
# 343 : : /**
# 344 : : * XOR the contents of this stream with a certain key.
# 345 : : *
# 346 : : * @param[in] key The key used to XOR the data in this stream.
# 347 : : */
# 348 : : void Xor(const std::vector<unsigned char>& key)
# 349 : 587529 : {
# 350 [ - + ]: 587529 : if (key.size() == 0) {
# 351 : 0 : return;
# 352 : 0 : }
# 353 : :
# 354 [ + + ]: 29330510 : for (size_type i = 0, j = 0; i != size(); i++) {
# 355 : 28742981 : vch[i] ^= std::byte{key[j++]};
# 356 : :
# 357 : : // This potentially acts on very many bytes of data, so it's
# 358 : : // important that we calculate `j`, i.e. the `key` index in this
# 359 : : // way instead of doing a %, which would effectively be a division
# 360 : : // for each byte Xor'd -- much slower than need be.
# 361 [ + + ]: 28742981 : if (j == key.size())
# 362 : 3342150 : j = 0;
# 363 : 28742981 : }
# 364 : 587529 : }
# 365 : : };
# 366 : :
# 367 : : template <typename IStream>
# 368 : : class BitStreamReader
# 369 : : {
# 370 : : private:
# 371 : : IStream& m_istream;
# 372 : :
# 373 : : /// Buffered byte read in from the input stream. A new byte is read into the
# 374 : : /// buffer when m_offset reaches 8.
# 375 : : uint8_t m_buffer{0};
# 376 : :
# 377 : : /// Number of high order bits in m_buffer already returned by previous
# 378 : : /// Read() calls. The next bit to be returned is at this offset from the
# 379 : : /// most significant bit position.
# 380 : : int m_offset{8};
# 381 : :
# 382 : : public:
# 383 : 1115 : explicit BitStreamReader(IStream& istream) : m_istream(istream) {}
# 384 : :
# 385 : : /** Read the specified number of bits from the stream. The data is returned
# 386 : : * in the nbits least significant bits of a 64-bit uint.
# 387 : : */
# 388 : 47046 : uint64_t Read(int nbits) {
# 389 [ - + ][ - + ]: 47046 : if (nbits < 0 || nbits > 64) {
# [ - + ][ - + ]
# 390 : 0 : throw std::out_of_range("nbits must be between 0 and 64");
# 391 : 0 : }
# 392 : :
# 393 : 47046 : uint64_t data = 0;
# 394 [ + + ][ + + ]: 116840 : while (nbits > 0) {
# 395 [ + + ][ + + ]: 69794 : if (m_offset == 8) {
# 396 : 28537 : m_istream >> m_buffer;
# 397 : 28537 : m_offset = 0;
# 398 : 28537 : }
# 399 : :
# 400 : 69794 : int bits = std::min(8 - m_offset, nbits);
# 401 : 69794 : data <<= bits;
# 402 : 69794 : data |= static_cast<uint8_t>(m_buffer << m_offset) >> (8 - bits);
# 403 : 69794 : m_offset += bits;
# 404 : 69794 : nbits -= bits;
# 405 : 69794 : }
# 406 : 47046 : return data;
# 407 : 47046 : }
# 408 : : };
# 409 : :
# 410 : : template <typename OStream>
# 411 : : class BitStreamWriter
# 412 : : {
# 413 : : private:
# 414 : : OStream& m_ostream;
# 415 : :
# 416 : : /// Buffered byte waiting to be written to the output stream. The byte is
# 417 : : /// written buffer when m_offset reaches 8 or Flush() is called.
# 418 : : uint8_t m_buffer{0};
# 419 : :
# 420 : : /// Number of high order bits in m_buffer already written by previous
# 421 : : /// Write() calls and not yet flushed to the stream. The next bit to be
# 422 : : /// written to is at this offset from the most significant bit position.
# 423 : : int m_offset{0};
# 424 : :
# 425 : : public:
# 426 : 6598 : explicit BitStreamWriter(OStream& ostream) : m_ostream(ostream) {}
# 427 : :
# 428 : : ~BitStreamWriter()
# 429 : 6598 : {
# 430 : 6598 : Flush();
# 431 : 6598 : }
# 432 : :
# 433 : : /** Write the nbits least significant bits of a 64-bit int to the output
# 434 : : * stream. Data is buffered until it completes an octet.
# 435 : : */
# 436 : 16084 : void Write(uint64_t data, int nbits) {
# 437 [ - + ][ - + ]: 16084 : if (nbits < 0 || nbits > 64) {
# [ - + ][ - + ]
# 438 : 0 : throw std::out_of_range("nbits must be between 0 and 64");
# 439 : 0 : }
# 440 : :
# 441 [ + + ][ + + ]: 45771 : while (nbits > 0) {
# 442 : 29687 : int bits = std::min(8 - m_offset, nbits);
# 443 : 29687 : m_buffer |= (data << (64 - nbits)) >> (64 - 8 + m_offset);
# 444 : 29687 : m_offset += bits;
# 445 : 29687 : nbits -= bits;
# 446 : :
# 447 [ + + ][ + + ]: 29687 : if (m_offset == 8) {
# 448 : 13681 : Flush();
# 449 : 13681 : }
# 450 : 29687 : }
# 451 : 16084 : }
# 452 : :
# 453 : : /** Flush any unwritten bits to the output stream, padding with 0's to the
# 454 : : * next byte boundary.
# 455 : : */
# 456 : 26877 : void Flush() {
# 457 [ + + ][ + + ]: 26877 : if (m_offset == 0) {
# 458 : 6602 : return;
# 459 : 6602 : }
# 460 : :
# 461 : 20275 : m_ostream << m_buffer;
# 462 : 20275 : m_buffer = 0;
# 463 : 20275 : m_offset = 0;
# 464 : 20275 : }
# 465 : : };
# 466 : :
# 467 : :
# 468 : :
# 469 : : /** Non-refcounted RAII wrapper for FILE*
# 470 : : *
# 471 : : * Will automatically close the file when it goes out of scope if not null.
# 472 : : * If you're returning the file pointer, return file.release().
# 473 : : * If you need to close the file early, use file.fclose() instead of fclose(file).
# 474 : : */
# 475 : : class CAutoFile
# 476 : : {
# 477 : : private:
# 478 : : const int nType;
# 479 : : const int nVersion;
# 480 : :
# 481 : : FILE* file;
# 482 : :
# 483 : : public:
# 484 : : CAutoFile(FILE* filenew, int nTypeIn, int nVersionIn) : nType(nTypeIn), nVersion(nVersionIn)
# 485 : 291431 : {
# 486 : 291431 : file = filenew;
# 487 : 291431 : }
# 488 : :
# 489 : : ~CAutoFile()
# 490 : 291429 : {
# 491 : 291429 : fclose();
# 492 : 291429 : }
# 493 : :
# 494 : : // Disallow copies
# 495 : : CAutoFile(const CAutoFile&) = delete;
# 496 : : CAutoFile& operator=(const CAutoFile&) = delete;
# 497 : :
# 498 : : void fclose()
# 499 : 293320 : {
# 500 [ + + ]: 293320 : if (file) {
# 501 : 289837 : ::fclose(file);
# 502 : 289837 : file = nullptr;
# 503 : 289837 : }
# 504 : 293320 : }
# 505 : :
# 506 : : /** Get wrapped FILE* with transfer of ownership.
# 507 : : * @note This will invalidate the CAutoFile object, and makes it the responsibility of the caller
# 508 : : * of this function to clean up the returned FILE*.
# 509 : : */
# 510 : 0 : FILE* release() { FILE* ret = file; file = nullptr; return ret; }
# 511 : :
# 512 : : /** Get wrapped FILE* without transfer of ownership.
# 513 : : * @note Ownership of the FILE* will remain with this class. Use this only if the scope of the
# 514 : : * CAutoFile outlives use of the passed pointer.
# 515 : : */
# 516 : 125497 : FILE* Get() const { return file; }
# 517 : :
# 518 : : /** Return true if the wrapped FILE* is nullptr, false otherwise.
# 519 : : */
# 520 : 290651 : bool IsNull() const { return (file == nullptr); }
# 521 : :
# 522 : : //
# 523 : : // Stream subset
# 524 : : //
# 525 : 21125 : int GetType() const { return nType; }
# 526 : 433541 : int GetVersion() const { return nVersion; }
# 527 : :
# 528 : : void read(Span<std::byte> dst)
# 529 : 18151820 : {
# 530 [ - + ]: 18151820 : if (!file)
# 531 : 0 : throw std::ios_base::failure("CAutoFile::read: file handle is nullptr");
# 532 [ + + ]: 18151820 : if (fread(dst.data(), 1, dst.size(), file) != dst.size()) {
# 533 [ + - ]: 8 : throw std::ios_base::failure(feof(file) ? "CAutoFile::read: end of file" : "CAutoFile::read: fread failed");
# 534 : 8 : }
# 535 : 18151820 : }
# 536 : :
# 537 : : void ignore(size_t nSize)
# 538 : 0 : {
# 539 [ # # ]: 0 : if (!file)
# 540 : 0 : throw std::ios_base::failure("CAutoFile::ignore: file handle is nullptr");
# 541 : 0 : unsigned char data[4096];
# 542 [ # # ]: 0 : while (nSize > 0) {
# 543 : 0 : size_t nNow = std::min<size_t>(nSize, sizeof(data));
# 544 [ # # ]: 0 : if (fread(data, 1, nNow, file) != nNow)
# 545 [ # # ]: 0 : throw std::ios_base::failure(feof(file) ? "CAutoFile::ignore: end of file" : "CAutoFile::read: fread failed");
# 546 : 0 : nSize -= nNow;
# 547 : 0 : }
# 548 : 0 : }
# 549 : :
# 550 : : void write(Span<const std::byte> src)
# 551 : 30477927 : {
# 552 [ - + ]: 30477927 : if (!file)
# 553 : 0 : throw std::ios_base::failure("CAutoFile::write: file handle is nullptr");
# 554 [ + + ]: 30477927 : if (fwrite(src.data(), 1, src.size(), file) != src.size()) {
# 555 : 2 : throw std::ios_base::failure("CAutoFile::write: write failed");
# 556 : 2 : }
# 557 : 30477927 : }
# 558 : :
# 559 : : template<typename T>
# 560 : : CAutoFile& operator<<(const T& obj)
# 561 : 24655084 : {
# 562 : : // Serialize to this stream
# 563 [ - + ][ - + ]: 24655084 : if (!file)
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# 564 : 0 : throw std::ios_base::failure("CAutoFile::operator<<: file handle is nullptr");
# 565 : 24655084 : ::Serialize(*this, obj);
# 566 : 24655084 : return (*this);
# 567 : 24655084 : }
# 568 : :
# 569 : : template<typename T>
# 570 : : CAutoFile& operator>>(T&& obj)
# 571 : 11558641 : {
# 572 : : // Unserialize from this stream
# 573 [ - + ][ - + ]: 11558641 : if (!file)
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ # # ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ][ - + ]
# [ - + ]
# 574 : 0 : throw std::ios_base::failure("CAutoFile::operator>>: file handle is nullptr");
# 575 : 11558641 : ::Unserialize(*this, obj);
# 576 : 11558641 : return (*this);
# 577 : 11558641 : }
# 578 : : };
# 579 : :
# 580 : : /** Non-refcounted RAII wrapper around a FILE* that implements a ring buffer to
# 581 : : * deserialize from. It guarantees the ability to rewind a given number of bytes.
# 582 : : *
# 583 : : * Will automatically close the file when it goes out of scope if not null.
# 584 : : * If you need to close the file early, use file.fclose() instead of fclose(file).
# 585 : : */
# 586 : : class CBufferedFile
# 587 : : {
# 588 : : private:
# 589 : : const int nType;
# 590 : : const int nVersion;
# 591 : :
# 592 : : FILE *src; //!< source file
# 593 : : uint64_t nSrcPos; //!< how many bytes have been read from source
# 594 : : uint64_t m_read_pos; //!< how many bytes have been read from this
# 595 : : uint64_t nReadLimit; //!< up to which position we're allowed to read
# 596 : : uint64_t nRewind; //!< how many bytes we guarantee to rewind
# 597 : : std::vector<std::byte> vchBuf; //!< the buffer
# 598 : :
# 599 : : protected:
# 600 : : //! read data from the source to fill the buffer
# 601 : 685 : bool Fill() {
# 602 : 685 : unsigned int pos = nSrcPos % vchBuf.size();
# 603 : 685 : unsigned int readNow = vchBuf.size() - pos;
# 604 : 685 : unsigned int nAvail = vchBuf.size() - (nSrcPos - m_read_pos) - nRewind;
# 605 [ + + ]: 685 : if (nAvail < readNow)
# 606 : 593 : readNow = nAvail;
# 607 [ - + ]: 685 : if (readNow == 0)
# 608 : 0 : return false;
# 609 : 685 : size_t nBytes = fread((void*)&vchBuf[pos], 1, readNow, src);
# 610 [ + + ]: 685 : if (nBytes == 0) {
# 611 [ + - ]: 11 : throw std::ios_base::failure(feof(src) ? "CBufferedFile::Fill: end of file" : "CBufferedFile::Fill: fread failed");
# 612 : 11 : }
# 613 : 674 : nSrcPos += nBytes;
# 614 : 674 : return true;
# 615 : 685 : }
# 616 : :
# 617 : : public:
# 618 : : CBufferedFile(FILE* fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn)
# 619 : : : nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), m_read_pos(0), nReadLimit(std::numeric_limits<uint64_t>::max()), nRewind(nRewindIn), vchBuf(nBufSize, std::byte{0})
# 620 : 114 : {
# 621 [ + + ]: 114 : if (nRewindIn >= nBufSize)
# 622 : 2 : throw std::ios_base::failure("Rewind limit must be less than buffer size");
# 623 : 112 : src = fileIn;
# 624 : 112 : }
# 625 : :
# 626 : : ~CBufferedFile()
# 627 : 112 : {
# 628 : 112 : fclose();
# 629 : 112 : }
# 630 : :
# 631 : : // Disallow copies
# 632 : : CBufferedFile(const CBufferedFile&) = delete;
# 633 : : CBufferedFile& operator=(const CBufferedFile&) = delete;
# 634 : :
# 635 : 1554 : int GetVersion() const { return nVersion; }
# 636 : 2 : int GetType() const { return nType; }
# 637 : :
# 638 : : void fclose()
# 639 : 114 : {
# 640 [ + + ]: 114 : if (src) {
# 641 : 112 : ::fclose(src);
# 642 : 112 : src = nullptr;
# 643 : 112 : }
# 644 : 114 : }
# 645 : :
# 646 : : //! check whether we're at the end of the source file
# 647 : 8799 : bool eof() const {
# 648 [ + + ][ + + ]: 8799 : return m_read_pos == nSrcPos && feof(src);
# 649 : 8799 : }
# 650 : :
# 651 : : //! read a number of bytes
# 652 : : void read(Span<std::byte> dst)
# 653 : 49774 : {
# 654 [ + + ]: 49774 : if (dst.size() + m_read_pos > nReadLimit) {
# 655 : 2 : throw std::ios_base::failure("Read attempted past buffer limit");
# 656 : 2 : }
# 657 [ + + ]: 99800 : while (dst.size() > 0) {
# 658 [ + + ]: 50028 : if (m_read_pos == nSrcPos)
# 659 : 416 : Fill();
# 660 : 50028 : unsigned int pos = m_read_pos % vchBuf.size();
# 661 : 50028 : size_t nNow = dst.size();
# 662 [ + + ]: 50028 : if (nNow + pos > vchBuf.size())
# 663 : 74 : nNow = vchBuf.size() - pos;
# 664 [ + + ]: 50028 : if (nNow + m_read_pos > nSrcPos)
# 665 : 196 : nNow = nSrcPos - m_read_pos;
# 666 : 50028 : memcpy(dst.data(), &vchBuf[pos], nNow);
# 667 : 50028 : m_read_pos += nNow;
# 668 : 50028 : dst = dst.subspan(nNow);
# 669 : 50028 : }
# 670 : 49772 : }
# 671 : :
# 672 : : //! return the current reading position
# 673 : 14569 : uint64_t GetPos() const {
# 674 : 14569 : return m_read_pos;
# 675 : 14569 : }
# 676 : :
# 677 : : //! rewind to a given reading position
# 678 : 2846 : bool SetPos(uint64_t nPos) {
# 679 : 2846 : size_t bufsize = vchBuf.size();
# 680 [ + + ]: 2846 : if (nPos + bufsize < nSrcPos) {
# 681 : : // rewinding too far, rewind as far as possible
# 682 : 102 : m_read_pos = nSrcPos - bufsize;
# 683 : 102 : return false;
# 684 : 102 : }
# 685 [ + + ]: 2744 : if (nPos > nSrcPos) {
# 686 : : // can't go this far forward, go as far as possible
# 687 : 36 : m_read_pos = nSrcPos;
# 688 : 36 : return false;
# 689 : 36 : }
# 690 : 2708 : m_read_pos = nPos;
# 691 : 2708 : return true;
# 692 : 2744 : }
# 693 : :
# 694 : : //! prevent reading beyond a certain position
# 695 : : //! no argument removes the limit
# 696 : 8831 : bool SetLimit(uint64_t nPos = std::numeric_limits<uint64_t>::max()) {
# 697 [ - + ]: 8831 : if (nPos < m_read_pos)
# 698 : 0 : return false;
# 699 : 8831 : nReadLimit = nPos;
# 700 : 8831 : return true;
# 701 : 8831 : }
# 702 : :
# 703 : : template<typename T>
# 704 : 21033 : CBufferedFile& operator>>(T&& obj) {
# 705 : : // Unserialize from this stream
# 706 : 21033 : ::Unserialize(*this, obj);
# 707 : 21033 : return (*this);
# 708 : 21033 : }
# 709 : :
# 710 : : //! search for a given byte in the stream, and remain positioned on it
# 711 : : void FindByte(uint8_t ch)
# 712 : 2942 : {
# 713 : 150630209 : while (true) {
# 714 [ + + ]: 150630200 : if (m_read_pos == nSrcPos)
# 715 : 269 : Fill();
# 716 [ + + ]: 150630200 : if (vchBuf[m_read_pos % vchBuf.size()] == std::byte{ch}) {
# 717 : 2933 : break;
# 718 : 2933 : }
# 719 : 150627267 : m_read_pos++;
# 720 : 150627267 : }
# 721 : 2942 : }
# 722 : : };
# 723 : :
# 724 : : #endif // BITCOIN_STREAMS_H
|