/ src / streams.cpp
streams.cpp
  1  // Copyright (c) 2009-present The Bitcoin Core developers
  2  // Distributed under the MIT software license, see the accompanying
  3  // file COPYING or https://opensource.org/license/mit/.
  4  
  5  #include <memusage.h>
  6  #include <span.h>
  7  #include <streams.h>
  8  #include <util/fs_helpers.h>
  9  #include <util/obfuscation.h>
 10  
 11  #include <array>
 12  
 13  AutoFile::AutoFile(std::FILE* file, const Obfuscation& obfuscation) : m_file{file}, m_obfuscation{obfuscation}
 14  {
 15      if (!IsNull()) {
 16          auto pos{std::ftell(m_file)};
 17          if (pos >= 0) m_position = pos;
 18      }
 19  }
 20  
 21  std::size_t AutoFile::detail_fread(std::span<std::byte> dst)
 22  {
 23      if (!m_file) throw std::ios_base::failure("AutoFile::read: file handle is nullptr");
 24      const size_t ret = std::fread(dst.data(), 1, dst.size(), m_file);
 25      if (m_obfuscation) {
 26          if (!m_position) throw std::ios_base::failure("AutoFile::read: position unknown");
 27          m_obfuscation(dst.subspan(0, ret), *m_position);
 28      }
 29      if (m_position) *m_position += ret;
 30      return ret;
 31  }
 32  
 33  void AutoFile::seek(int64_t offset, int origin)
 34  {
 35      if (IsNull()) {
 36          throw std::ios_base::failure("AutoFile::seek: file handle is nullptr");
 37      }
 38      if (std::fseek(m_file, offset, origin) != 0) {
 39          throw std::ios_base::failure(feof() ? "AutoFile::seek: end of file" : "AutoFile::seek: fseek failed");
 40      }
 41      if (origin == SEEK_SET) {
 42          m_position = offset;
 43      } else if (origin == SEEK_CUR && m_position.has_value()) {
 44          *m_position += offset;
 45      } else {
 46          int64_t r{std::ftell(m_file)};
 47          if (r < 0) {
 48              throw std::ios_base::failure("AutoFile::seek: ftell failed");
 49          }
 50          m_position = r;
 51      }
 52  }
 53  
 54  int64_t AutoFile::tell()
 55  {
 56      if (!m_position.has_value()) throw std::ios_base::failure("AutoFile::tell: position unknown");
 57      return *m_position;
 58  }
 59  
 60  int64_t AutoFile::size()
 61  {
 62      if (IsNull()) {
 63          throw std::ios_base::failure("AutoFile::size: file handle is nullptr");
 64      }
 65      // Temporarily save the current position
 66      int64_t current_pos = tell();
 67      seek(0, SEEK_END);
 68      int64_t file_size = tell();
 69      // Restore the original position
 70      seek(current_pos, SEEK_SET);
 71      return file_size;
 72  }
 73  
 74  void AutoFile::read(std::span<std::byte> dst)
 75  {
 76      if (detail_fread(dst) != dst.size()) {
 77          throw std::ios_base::failure(feof() ? "AutoFile::read: end of file" : "AutoFile::read: fread failed");
 78      }
 79  }
 80  
 81  void AutoFile::ignore(size_t nSize)
 82  {
 83      if (!m_file) throw std::ios_base::failure("AutoFile::ignore: file handle is nullptr");
 84      unsigned char data[4096];
 85      while (nSize > 0) {
 86          size_t nNow = std::min<size_t>(nSize, sizeof(data));
 87          if (std::fread(data, 1, nNow, m_file) != nNow) {
 88              throw std::ios_base::failure(feof() ? "AutoFile::ignore: end of file" : "AutoFile::ignore: fread failed");
 89          }
 90          nSize -= nNow;
 91          if (m_position.has_value()) *m_position += nNow;
 92      }
 93  }
 94  
 95  void AutoFile::write(std::span<const std::byte> src)
 96  {
 97      if (!m_file) throw std::ios_base::failure("AutoFile::write: file handle is nullptr");
 98      if (!m_obfuscation) {
 99          if (std::fwrite(src.data(), 1, src.size(), m_file) != src.size()) {
100              throw std::ios_base::failure("AutoFile::write: write failed");
101          }
102          m_was_written = true;
103          if (m_position.has_value()) *m_position += src.size();
104      } else {
105          std::array<std::byte, 4096> buf;
106          while (src.size()) {
107              auto buf_now{std::span{buf}.first(std::min<size_t>(src.size(), buf.size()))};
108              std::copy_n(src.begin(), buf_now.size(), buf_now.begin());
109              write_buffer(buf_now);
110              src = src.subspan(buf_now.size());
111          }
112      }
113  }
114  
115  void AutoFile::write_buffer(std::span<std::byte> src)
116  {
117      if (!m_file) throw std::ios_base::failure("AutoFile::write_buffer: file handle is nullptr");
118      if (m_obfuscation) {
119          if (!m_position) throw std::ios_base::failure("AutoFile::write_buffer: obfuscation position unknown");
120          m_obfuscation(src, *m_position); // obfuscate in-place
121      }
122      if (std::fwrite(src.data(), 1, src.size(), m_file) != src.size()) {
123          throw std::ios_base::failure("AutoFile::write_buffer: write failed");
124      }
125      m_was_written = true;
126      if (m_position) *m_position += src.size();
127  }
128  
129  bool AutoFile::Commit()
130  {
131      return ::FileCommit(m_file);
132  }
133  
134  bool AutoFile::Truncate(unsigned size)
135  {
136      m_was_written = true;
137      return ::TruncateFile(m_file, size);
138  }
139  
140  size_t DataStream::GetMemoryUsage() const noexcept
141  {
142      return sizeof(*this) + memusage::DynamicUsage(vch);
143  }