Generic binary file stream support (big-endian)
Change-Id: I27bd490103fd193e35b0bbda17f7a1c90b566ecc
Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
diff --git a/test/bin_stream_test.cpp b/test/bin_stream_test.cpp
new file mode 100644
index 0000000..209c778
--- /dev/null
+++ b/test/bin_stream_test.cpp
@@ -0,0 +1,71 @@
+#include <util/bin_stream.hpp>
+
+#include "gtest/gtest.h"
+
+enum RegisterId_t : uint32_t; // Defined in hei_types.hpp
+
+namespace util
+{
+
+/** @brief Extracts big-endian data to host RegisterId_t. */
+template <>
+inline BinFileReader& BinFileReader::operator>>(RegisterId_t& r)
+{
+ // A register ID is only 3 bytes, but there isn't a 3-byte integer type.
+ // So extract 3 bytes to a uint32_t and drop the unused byte.
+ uint32_t tmp = 0;
+ read(&tmp, 3);
+ r = static_cast<RegisterId_t>(be32toh(tmp) >> 8);
+ return *this;
+}
+
+/** @brief Inserts host RegisterId_t to big-endian data. */
+template <>
+inline BinFileWriter& BinFileWriter::operator<<(RegisterId_t r)
+{
+ // A register ID is only 3 bytes, but there isn't a 3-byte integer type.
+ // So extract 3 bytes to a uint32_t and drop the unused byte.
+ uint32_t tmp = htobe32(static_cast<uint32_t>(r) << 8);
+ write(&tmp, 3);
+ return *this;
+}
+
+} // namespace util
+
+TEST(BinStream, TestSet1)
+{
+ uint8_t w1 = 0x11;
+ uint16_t w2 = 0x1122;
+ uint32_t w3 = 0x11223344;
+ uint64_t w4 = 0x1122334455667788;
+ RegisterId_t w5 = static_cast<RegisterId_t>(0x123456);
+
+ {
+ // need scope so the writer is closed before the reader opens
+ util::BinFileWriter w{"bin_stream_test.bin"};
+ ASSERT_TRUE(w.good());
+
+ w << w1 << w2 << w3 << w4 << w5;
+ ASSERT_TRUE(w.good());
+ }
+
+ uint8_t r1;
+ uint16_t r2;
+ uint32_t r3;
+ uint64_t r4;
+ RegisterId_t r5;
+
+ {
+ util::BinFileReader r{"bin_stream_test.bin"};
+ ASSERT_TRUE(r.good());
+
+ r >> r1 >> r2 >> r3 >> r4 >> r5;
+ ASSERT_TRUE(r.good());
+ }
+
+ ASSERT_EQ(w1, r1);
+ ASSERT_EQ(w2, r2);
+ ASSERT_EQ(w3, r3);
+ ASSERT_EQ(w4, r4);
+ ASSERT_EQ(w5, r5);
+}
diff --git a/test/meson.build b/test/meson.build
index bf062ad..764ab9a 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -7,6 +7,7 @@
tests = [
'hello-world',
+ 'bin_stream_test',
]
gtest = dependency('gtest', main : true, required : false, method : 'system')
diff --git a/util/bin_stream.hpp b/util/bin_stream.hpp
new file mode 100644
index 0000000..bdf10e5
--- /dev/null
+++ b/util/bin_stream.hpp
@@ -0,0 +1,201 @@
+#pragma once
+
+#include <endian.h>
+#include <string.h>
+
+#include <fstream>
+
+namespace util
+{
+
+/**
+ * @brief A streaming utility to read a binary file.
+ * @note IMPORTANT: Assumes file data is in big-endian format.
+ */
+class BinFileReader
+{
+ public:
+ /**
+ * @brief Constructor.
+ * @param f The name of the target file.
+ */
+ explicit BinFileReader(const char* f) : iv_stream(f, std::ios::binary) {}
+
+ /** @brief Destructor. */
+ ~BinFileReader() = default;
+
+ /** @brief Copy constructor. */
+ BinFileReader(const BinFileReader&) = delete;
+
+ /** @brief Assignment operator. */
+ BinFileReader& operator=(const BinFileReader&) = delete;
+
+ private:
+ /** The input file stream. */
+ std::ifstream iv_stream;
+
+ public:
+ /** @return True, if the state of the stream is good. */
+ bool good()
+ {
+ return iv_stream.good();
+ }
+
+ /**
+ * @brief Extracts n characters from the stream and stores them in the array
+ * pointed to by s.
+ * @note This function simply copies a block of data without checking its
+ * contents or endianness.
+ * @note After calling, check good() to determine if the operation was
+ * successful.
+ * @param s Pointer to an array of at least n characters.
+ * @param n Number of characters to extract.
+ */
+ void read(void* s, size_t n)
+ {
+ iv_stream.read(static_cast<char*>(s), n);
+ }
+
+ /**
+ * @brief Input stream operator.
+ * @note The default template is intentionally not defined so that only
+ * specializations of this function can be used. This avoids
+ * accidental usage on objects where endianness is a concern.
+ * @note This is written as a template so that users can define their own
+ * specializations for non-standard types.
+ */
+ template <class D>
+ BinFileReader& operator>>(D& r);
+};
+
+/** @brief Extracts big-endian data to host uint8_t. */
+template <>
+inline BinFileReader& BinFileReader::operator>>(uint8_t& r)
+{
+ read(&r, sizeof(r));
+ return *this;
+}
+
+/** @brief Extracts big-endian data to host uint16_t. */
+template <>
+inline BinFileReader& BinFileReader::operator>>(uint16_t& r)
+{
+ read(&r, sizeof(r));
+ r = be16toh(r);
+ return *this;
+}
+
+/** @brief Extracts big-endian data to host uint32_t. */
+template <>
+inline BinFileReader& BinFileReader::operator>>(uint32_t& r)
+{
+ read(&r, sizeof(r));
+ r = be32toh(r);
+ return *this;
+}
+
+/** @brief Extracts big-endian data to host uint64_t. */
+template <>
+inline BinFileReader& BinFileReader::operator>>(uint64_t& r)
+{
+ read(&r, sizeof(r));
+ r = be64toh(r);
+ return *this;
+}
+
+/**
+ * @brief A streaming utility to write a binary file.
+ * @note IMPORTANT: Assumes file data is in big-endian format.
+ */
+class BinFileWriter
+{
+ public:
+ /**
+ * @brief Constructor.
+ * @param f The name of the target file.
+ */
+ explicit BinFileWriter(const char* f) : iv_stream(f, std::ios::binary) {}
+
+ /** @brief Destructor. */
+ ~BinFileWriter() = default;
+
+ /** @brief Copy constructor. */
+ BinFileWriter(const BinFileWriter&) = delete;
+
+ /** @brief Assignment operator. */
+ BinFileWriter& operator=(const BinFileWriter&) = delete;
+
+ private:
+ /** The output file stream. */
+ std::ofstream iv_stream;
+
+ public:
+ /** @return True, if the state of the stream is good. */
+ bool good()
+ {
+ return iv_stream.good();
+ }
+
+ /**
+ * @brief Inserts the first n characters of the the array pointed to by s
+ into the stream.
+ * @note This function simply copies a block of data without checking its
+ * contents or endianness.
+ * @note After calling, check good() to determine if the operation was
+ * successful.
+ * @param s Pointer to an array of at least n characters.
+ * @param n Number of characters to insert.
+ */
+ void write(void* s, size_t n)
+ {
+ iv_stream.write(static_cast<char*>(s), n);
+ }
+
+ /**
+ * @brief Output stream operator.
+ * @note The default template is intentionally not defined so that only
+ * specializations of this function can be used. This avoids
+ * accidental usage on objects where endianness is a concern.
+ * @note This is written as a template so that users can define their own
+ * specializations for non-standard types.
+ */
+ template <class D>
+ BinFileWriter& operator<<(D r);
+};
+
+/** @brief Inserts host uint8_t to big-endian data. */
+template <>
+inline BinFileWriter& BinFileWriter::operator<<(uint8_t r)
+{
+ write(&r, sizeof(r));
+ return *this;
+}
+
+/** @brief Inserts host uint16_t to big-endian data. */
+template <>
+inline BinFileWriter& BinFileWriter::operator<<(uint16_t r)
+{
+ r = htobe16(r);
+ write(&r, sizeof(r));
+ return *this;
+}
+
+/** @brief Inserts host uint32_t to big-endian data. */
+template <>
+inline BinFileWriter& BinFileWriter::operator<<(uint32_t r)
+{
+ r = htobe32(r);
+ write(&r, sizeof(r));
+ return *this;
+}
+
+/** @brief Inserts host uint64_t to big-endian data. */
+template <>
+inline BinFileWriter& BinFileWriter::operator<<(uint64_t r)
+{
+ r = htobe64(r);
+ write(&r, sizeof(r));
+ return *this;
+}
+
+} // namespace util