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 */
BinFileReader(const std::filesystem::path & p)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. */
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 */
read(void * s,size_t n)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 <>
operator >>(uint8_t & r)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 <>
operator >>(uint16_t & r)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 <>
operator >>(uint32_t & r)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 <>
operator >>(uint64_t & r)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 */
BinFileWriter(const std::filesystem::path & p)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. */
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 */
write(void * s,size_t n)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 <>
operator <<(uint8_t r)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 <>
operator <<(uint16_t r)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 <>
operator <<(uint32_t r)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 <>
operator <<(uint64_t r)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