1 #pragma once 2 3 #include <endian.h> 4 #include <string.h> 5 6 #include <filesystem> 7 #include <fstream> 8 9 namespace util 10 { 11 12 /** 13 * @brief A streaming utility to read a binary file. 14 * @note IMPORTANT: Assumes file data is in big-endian format. 15 */ 16 class BinFileReader 17 { 18 public: 19 /** 20 * @brief Constructor. 21 * @param f The name of the target file. 22 */ 23 explicit BinFileReader(const std::filesystem::path& p) : 24 iv_stream(p, std::ios::binary) 25 {} 26 27 /** @brief Destructor. */ 28 ~BinFileReader() = default; 29 30 /** @brief Copy constructor. */ 31 BinFileReader(const BinFileReader&) = delete; 32 33 /** @brief Assignment operator. */ 34 BinFileReader& operator=(const BinFileReader&) = delete; 35 36 private: 37 /** The input file stream. */ 38 std::ifstream iv_stream; 39 40 public: 41 /** @return True, if the state of the stream is good. */ 42 bool good() 43 { 44 return iv_stream.good(); 45 } 46 47 /** 48 * @brief Extracts n characters from the stream and stores them in the array 49 * pointed to by s. 50 * @note This function simply copies a block of data without checking its 51 * contents or endianness. 52 * @note After calling, check good() to determine if the operation was 53 * successful. 54 * @param s Pointer to an array of at least n characters. 55 * @param n Number of characters to extract. 56 */ 57 void read(void* s, size_t n) 58 { 59 iv_stream.read(static_cast<char*>(s), n); 60 } 61 62 /** 63 * @brief Input stream operator. 64 * @note The default template is intentionally not defined so that only 65 * specializations of this function can be used. This avoids 66 * accidental usage on objects where endianness is a concern. 67 * @note This is written as a template so that users can define their own 68 * specializations for non-standard types. 69 */ 70 template <class D> 71 BinFileReader& operator>>(D& r); 72 }; 73 74 /** @brief Extracts big-endian data to host uint8_t. */ 75 template <> 76 inline BinFileReader& BinFileReader::operator>>(uint8_t& r) 77 { 78 read(&r, sizeof(r)); 79 return *this; 80 } 81 82 /** @brief Extracts big-endian data to host uint16_t. */ 83 template <> 84 inline BinFileReader& BinFileReader::operator>>(uint16_t& r) 85 { 86 read(&r, sizeof(r)); 87 r = be16toh(r); 88 return *this; 89 } 90 91 /** @brief Extracts big-endian data to host uint32_t. */ 92 template <> 93 inline BinFileReader& BinFileReader::operator>>(uint32_t& r) 94 { 95 read(&r, sizeof(r)); 96 r = be32toh(r); 97 return *this; 98 } 99 100 /** @brief Extracts big-endian data to host uint64_t. */ 101 template <> 102 inline BinFileReader& BinFileReader::operator>>(uint64_t& r) 103 { 104 read(&r, sizeof(r)); 105 r = be64toh(r); 106 return *this; 107 } 108 109 /** 110 * @brief A streaming utility to write a binary file. 111 * @note IMPORTANT: Assumes file data is in big-endian format. 112 */ 113 class BinFileWriter 114 { 115 public: 116 /** 117 * @brief Constructor. 118 * @param f The name of the target file. 119 */ 120 explicit BinFileWriter(const std::filesystem::path& p) : 121 iv_stream(p, std::ios::binary) 122 {} 123 124 /** @brief Destructor. */ 125 ~BinFileWriter() = default; 126 127 /** @brief Copy constructor. */ 128 BinFileWriter(const BinFileWriter&) = delete; 129 130 /** @brief Assignment operator. */ 131 BinFileWriter& operator=(const BinFileWriter&) = delete; 132 133 private: 134 /** The output file stream. */ 135 std::ofstream iv_stream; 136 137 public: 138 /** @return True, if the state of the stream is good. */ 139 bool good() 140 { 141 return iv_stream.good(); 142 } 143 144 /** 145 * @brief Inserts the first n characters of the the array pointed to by s 146 into the stream. 147 * @note This function simply copies a block of data without checking its 148 * contents or endianness. 149 * @note After calling, check good() to determine if the operation was 150 * successful. 151 * @param s Pointer to an array of at least n characters. 152 * @param n Number of characters to insert. 153 */ 154 void write(void* s, size_t n) 155 { 156 iv_stream.write(static_cast<char*>(s), n); 157 } 158 159 /** 160 * @brief Output stream operator. 161 * @note The default template is intentionally not defined so that only 162 * specializations of this function can be used. This avoids 163 * accidental usage on objects where endianness is a concern. 164 * @note This is written as a template so that users can define their own 165 * specializations for non-standard types. 166 */ 167 template <class D> 168 BinFileWriter& operator<<(D r); 169 }; 170 171 /** @brief Inserts host uint8_t to big-endian data. */ 172 template <> 173 inline BinFileWriter& BinFileWriter::operator<<(uint8_t r) 174 { 175 write(&r, sizeof(r)); 176 return *this; 177 } 178 179 /** @brief Inserts host uint16_t to big-endian data. */ 180 template <> 181 inline BinFileWriter& BinFileWriter::operator<<(uint16_t r) 182 { 183 r = htobe16(r); 184 write(&r, sizeof(r)); 185 return *this; 186 } 187 188 /** @brief Inserts host uint32_t to big-endian data. */ 189 template <> 190 inline BinFileWriter& BinFileWriter::operator<<(uint32_t r) 191 { 192 r = htobe32(r); 193 write(&r, sizeof(r)); 194 return *this; 195 } 196 197 /** @brief Inserts host uint64_t to big-endian data. */ 198 template <> 199 inline BinFileWriter& BinFileWriter::operator<<(uint64_t r) 200 { 201 r = htobe64(r); 202 write(&r, sizeof(r)); 203 return *this; 204 } 205 206 } // namespace util 207