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