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 10 #include <array> 11 12 AutoFile::AutoFile(std::FILE* file, std::vector<std::byte> data_xor) 13 : m_file{file}, m_xor{std::move(data_xor)} 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 size_t ret = std::fread(dst.data(), 1, dst.size(), m_file); 25 if (!m_xor.empty()) { 26 if (!m_position.has_value()) throw std::ios_base::failure("AutoFile::read: position unknown"); 27 util::Xor(dst.subspan(0, ret), m_xor, *m_position); 28 } 29 if (m_position.has_value()) *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 void AutoFile::read(std::span<std::byte> dst) 61 { 62 if (detail_fread(dst) != dst.size()) { 63 throw std::ios_base::failure(feof() ? "AutoFile::read: end of file" : "AutoFile::read: fread failed"); 64 } 65 } 66 67 void AutoFile::ignore(size_t nSize) 68 { 69 if (!m_file) throw std::ios_base::failure("AutoFile::ignore: file handle is nullptr"); 70 unsigned char data[4096]; 71 while (nSize > 0) { 72 size_t nNow = std::min<size_t>(nSize, sizeof(data)); 73 if (std::fread(data, 1, nNow, m_file) != nNow) { 74 throw std::ios_base::failure(feof() ? "AutoFile::ignore: end of file" : "AutoFile::ignore: fread failed"); 75 } 76 nSize -= nNow; 77 if (m_position.has_value()) *m_position += nNow; 78 } 79 } 80 81 void AutoFile::write(std::span<const std::byte> src) 82 { 83 if (!m_file) throw std::ios_base::failure("AutoFile::write: file handle is nullptr"); 84 if (m_xor.empty()) { 85 if (std::fwrite(src.data(), 1, src.size(), m_file) != src.size()) { 86 throw std::ios_base::failure("AutoFile::write: write failed"); 87 } 88 if (m_position.has_value()) *m_position += src.size(); 89 } else { 90 std::array<std::byte, 4096> buf; 91 while (src.size()) { 92 auto buf_now{std::span{buf}.first(std::min<size_t>(src.size(), buf.size()))}; 93 std::copy_n(src.begin(), buf_now.size(), buf_now.begin()); 94 write_buffer(buf_now); 95 src = src.subspan(buf_now.size()); 96 } 97 } 98 } 99 100 void AutoFile::write_buffer(std::span<std::byte> src) 101 { 102 if (!m_file) throw std::ios_base::failure("AutoFile::write_buffer: file handle is nullptr"); 103 if (m_xor.size()) { 104 if (!m_position) throw std::ios_base::failure("AutoFile::write_buffer: obfuscation position unknown"); 105 util::Xor(src, m_xor, *m_position); // obfuscate in-place 106 } 107 if (std::fwrite(src.data(), 1, src.size(), m_file) != src.size()) { 108 throw std::ios_base::failure("AutoFile::write_buffer: write failed"); 109 } 110 if (m_position) *m_position += src.size(); 111 } 112 113 bool AutoFile::Commit() 114 { 115 return ::FileCommit(m_file); 116 } 117 118 bool AutoFile::Truncate(unsigned size) 119 { 120 return ::TruncateFile(m_file, size); 121 } 122 123 size_t DataStream::GetMemoryUsage() const noexcept 124 { 125 return sizeof(*this) + memusage::DynamicUsage(vch); 126 }