Skip to content

Commit

Permalink
Add methods to read 2D types from a binary file
Browse files Browse the repository at this point in the history
The underlying can memory order be specified by an enum.
Refs #11056
  • Loading branch information
martyngigg committed Dec 9, 2015
1 parent df5e621 commit d613c53
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 13 deletions.
31 changes: 23 additions & 8 deletions Framework/Kernel/inc/MantidKernel/BinaryStreamReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
namespace Mantid {
namespace Kernel {

// Forward declarations
template <typename T> class Matrix;

/**
* Assists with reading a binary file by providing standard overloads for the
* istream operators (>>) to given types (and vectors of those types). It
Expand Down Expand Up @@ -43,6 +46,9 @@ namespace Kernel {
*/
class MANTID_KERNEL_DLL BinaryStreamReader {
public:
/// Define the ordering of 2D structures in the file
enum class MatrixOrdering { RowMajor, ColumnMajor };

BinaryStreamReader(std::istream &istrm);
~BinaryStreamReader();

Expand All @@ -55,19 +61,28 @@ class MANTID_KERNEL_DLL BinaryStreamReader {
BinaryStreamReader &operator>>(std::string &value);
/// @}

///@name std::vector-reader methods
///@name 1D methods
/// @{
BinaryStreamReader & read(std::vector<int32_t> &value, const size_t nvals);
BinaryStreamReader & read(std::vector<int64_t> &value, const size_t nvals);
BinaryStreamReader & read(std::vector<float> &value, const size_t nvals);
BinaryStreamReader & read(std::vector<double> &value, const size_t nvals);
BinaryStreamReader &read(std::vector<int32_t> &value, const size_t nvals);
BinaryStreamReader &read(std::vector<int64_t> &value, const size_t nvals);
BinaryStreamReader &read(std::vector<float> &value, const size_t nvals);
BinaryStreamReader &read(std::vector<double> &value, const size_t nvals);
BinaryStreamReader &read(std::string &value, const size_t length);
/// @}

///@name std::string methods for specifying number of characters
///@name 2D methods
/// @{
BinaryStreamReader & read(std::string &value, const size_t length);
BinaryStreamReader &read(std::vector<std::string> &value,
const std::vector<int32_t> &shape,
MatrixOrdering order);
BinaryStreamReader &read(Kernel::Matrix<float> &value,
const std::vector<int32_t> &shape,
MatrixOrdering order);
BinaryStreamReader &read(Kernel::Matrix<double> &value,
const std::vector<int32_t> &shape,
MatrixOrdering order);
/// @}

private:
/// Reference to the stream being read
std::istream &m_istrm;
Expand Down
115 changes: 112 additions & 3 deletions Framework/Kernel/src/BinaryStreamReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
//------------------------------------------------------------------------------
#include "MantidKernel/BinaryStreamReader.h"
#include "MantidKernel/Exception.h"
#include "MantidKernel/Matrix.h"

#include <cassert>
#include <istream>

namespace Mantid {
namespace Kernel {

//------------------------------------------------------------------------------
// Anonymous functions
//------------------------------------------------------------------------------
Expand Down Expand Up @@ -36,10 +41,43 @@ inline void readFromStream(std::istream &stream, std::vector<T> &value,
value.resize(nvals);
stream.read(reinterpret_cast<char *>(value.data()), nvals * sizeof(T));
}
}

namespace Mantid {
namespace Kernel {
/**
* Read a stream of numbers and interpret them as a 2D matrix of specified
* shape & order
* @param stream The open stream on which to perform the read
* @param value The Matrix to fill. Its size is increased if necessary
* @param shape 2D-vector defined as (nrows, ncols)
* @param order Defines whether the stream of bytes is interpreted as moving
* across the rows first (RowMajor) or down columns first (ColumnMajor)
*/
template <typename T>
inline void readFromStream(std::istream &stream, Matrix<T> &value,
const std::vector<int32_t> &shape,
BinaryStreamReader::MatrixOrdering order) {
assert(2 <= shape.size());
const size_t s0(shape[0]), s1(shape[1]), totalLength(s0 * s1);
std::vector<T> buffer;
readFromStream(stream, buffer, totalLength);
value = Matrix<T>(s0, s1);
if (order == BinaryStreamReader::MatrixOrdering::RowMajor) {
for (size_t i = 0; i < s0; ++i) {
auto row = value[i];
const size_t offset = i * s1;
for (size_t j = 0; j < s1; ++j) {
row[j] = buffer[offset + j];
}
}
} else {
for (size_t i = 0; i < s0; ++i) {
auto row = value[i];
for (size_t j = 0; j < s1; ++j) {
row[j] = buffer[j * s0 + i];
}
}
}
}
}

//------------------------------------------------------------------------------
// Public members
Expand Down Expand Up @@ -185,5 +223,76 @@ BinaryStreamReader &BinaryStreamReader::read(std::string &value,
return *this;
}

/**
* Read a stream of characters and interpret them as a 2D matrix of specified
* shape & order
* @param value The array to fill. Its size is increased if necessary
* @param shape 2D-vector defined as (nstrs, strLength)
* @param order Defines whether the stream of bytes is interpreted as moving
* across the rows first (RowMajor) or down columns first (ColumnMajor)
* @return A reference to the BinaryStreamReader object
*/
BinaryStreamReader &
BinaryStreamReader::read(std::vector<std::string> &value,
const std::vector<int32_t> &shape,
BinaryStreamReader::MatrixOrdering order) {
assert(2 <= shape.size());

const size_t s0(shape[0]), s1(shape[1]), totalLength(s0 * s1);
std::string buffer;
this->read(buffer, totalLength);
value.resize(s0);
if (order == MatrixOrdering::RowMajor) {
size_t pos(0);
for (auto &str : value) {
str = buffer.substr(pos, s1);
pos += s1;
}
} else {
for (size_t i = 0; i < s0; ++i) {
auto &str = value[i];
str.resize(s1);
for (size_t j = 0; j < s1; ++j) {
str[j] = buffer[j * s0 + i];
}
}
}
return *this;
}

/**
* Read a stream of floats and interpret them as a 2D matrix of specified
* shape & order
* @param value The Matrix to fill. Its size is increased if necessary
* @param shape 2D-vector defined as (nrows, ncols)
* @param order Defines whether the stream of bytes is interpreted as moving
* across the rows first (RowMajor) or down columns first (ColumnMajor)
* @return A reference to the BinaryStreamReader object
*/
BinaryStreamReader &
BinaryStreamReader::read(Kernel::Matrix<float> &value,
const std::vector<int32_t> &shape,
BinaryStreamReader::MatrixOrdering order) {
readFromStream(m_istrm, value, shape, order);
return *this;
}

/**
* Read a stream of doubles and interpret them as a 2D matrix of specified
* shape & order
* @param value The Matrix to fill. Its size is increased if necessary
* @param shape 2D-vector defined as (nrows, ncols)
* @param order Defines whether the stream of bytes is interpreted as moving
* across the rows first (RowMajor) or down columns first (ColumnMajor)
* @return A reference to the BinaryStreamReader object
*/
BinaryStreamReader &
BinaryStreamReader::read(Kernel::Matrix<double> &value,
const std::vector<int32_t> &shape,
BinaryStreamReader::MatrixOrdering order) {
readFromStream(m_istrm, value, shape, order);
return *this;
}

} // namespace Kernel
} // namespace Mantid
105 changes: 103 additions & 2 deletions Framework/Kernel/test/BinaryStreamReaderTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
#define MANTID_KERNEL_BINARYSTREAMREADERTEST_H_

#include <cxxtest/TestSuite.h>
#include <cxxtest/ValueTraits.h>

#include "MantidKernel/BinaryStreamReader.h"
#include "MantidKernel/Matrix.h"

#include <sstream>

using Mantid::Kernel::BinaryStreamReader;
using Mantid::Kernel::Matrix;

class BinaryStreamReaderTest : public CxxTest::TestSuite {
public:
Expand All @@ -18,8 +21,7 @@ class BinaryStreamReaderTest : public CxxTest::TestSuite {
}
static void destroySuite(BinaryStreamReaderTest *suite) { delete suite; }

BinaryStreamReaderTest()
: CxxTest::TestSuite(), m_bytes() {
BinaryStreamReaderTest() : CxxTest::TestSuite(), m_bytes() {
createTestStream();
}

Expand Down Expand Up @@ -87,6 +89,98 @@ class BinaryStreamReaderTest : public CxxTest::TestSuite {
doReadArrayValueTest(nvals, expectedValue, nvals * sizeof(double));
}

void test_Read_Vector_String_In_Row_Major_Order() {
moveStreamToPosition(118);

BinaryStreamReader reader(m_bytes);
std::vector<std::string> expected{"abc", "def"}, value;
std::vector<int32_t> shape{2, 3};
reader.read(value, shape, BinaryStreamReader::MatrixOrdering::RowMajor);
TS_ASSERT_EQUALS(expected, value);
}

void test_Read_Vector_String_In_Column_Major_Order() {
moveStreamToPosition(118);

BinaryStreamReader reader(m_bytes);
std::vector<std::string> expected{"ace", "bdf"}, value;
std::vector<int32_t> shape{2, 3};
reader.read(value, shape, BinaryStreamReader::MatrixOrdering::ColumnMajor);
TS_ASSERT_EQUALS(expected, value);
}

void test_Read_Matrix_Float_In_RowMajor_Order() {
moveStreamToPosition(124);

BinaryStreamReader reader(m_bytes);
std::vector<int32_t> shape{2, 3};
Matrix<float> expected(shape[0], shape[1]);
expected[0][0] = 1.0f;
expected[0][1] = 2.0f;
expected[0][2] = 3.0f;
expected[1][0] = 4.0f;
expected[1][1] = 5.0f;
expected[1][2] = 6.0f;

Matrix<float> value;
reader.read(value, shape, BinaryStreamReader::MatrixOrdering::RowMajor);
TS_ASSERT_EQUALS(expected, value);
}

void test_Read_Matrix_Float_In_ColumnMajor_Order() {
moveStreamToPosition(124);

BinaryStreamReader reader(m_bytes);
std::vector<int32_t> shape{2, 3};
Matrix<float> expected(shape[0], shape[1]);
expected[0][0] = 1.0f;
expected[0][1] = 3.0f;
expected[0][2] = 5.0f;
expected[1][0] = 2.0f;
expected[1][1] = 4.0f;
expected[1][2] = 6.0f;

Matrix<float> value;
reader.read(value, shape, BinaryStreamReader::MatrixOrdering::ColumnMajor);
TS_ASSERT_EQUALS(expected, value);
}

void test_Read_Matrix_Double_In_RowMajor_Order() {
moveStreamToPosition(148);

BinaryStreamReader reader(m_bytes);
std::vector<int32_t> shape{2, 3};
Matrix<double> expected(shape[0], shape[1]);
expected[0][0] = 1.0;
expected[0][1] = 2.0;
expected[0][2] = 3.0;
expected[1][0] = 4.0;
expected[1][1] = 5.0;
expected[1][2] = 6.0;

Matrix<double> value;
reader.read(value, shape, BinaryStreamReader::MatrixOrdering::RowMajor);
TS_ASSERT_EQUALS(expected, value);
}

void test_Read_Matrix_Double_In_ColumnMajor_Order() {
moveStreamToPosition(148);

BinaryStreamReader reader(m_bytes);
std::vector<int32_t> shape{2, 3};
Matrix<double> expected(shape[0], shape[1]);
expected[0][0] = 1.0;
expected[0][1] = 3.0;
expected[0][2] = 5.0;
expected[1][0] = 2.0;
expected[1][1] = 4.0;
expected[1][2] = 6.0;

Matrix<double> value;
reader.read(value, shape, BinaryStreamReader::MatrixOrdering::ColumnMajor);
TS_ASSERT_EQUALS(expected, value);
}

// Only test this for a single type assuming it is the same for all
void test_Read_Vector_With_Bigger_Vector_Leaves_Size_Untouched() {
moveStreamToPosition(30);
Expand Down Expand Up @@ -174,6 +268,13 @@ class BinaryStreamReaderTest : public CxxTest::TestSuite {
writeArrayValuesToStream<float>(m_bytes, {0.0f, 5.0f, 10.0f});
// vector double
writeArrayValuesToStream<double>(m_bytes, {10.0, 15.0, 20.0, 25.0});
// array of characters
m_bytes.write("abcdef", 6);
// matrix of floats
writeArrayValuesToStream<float>(m_bytes,
{1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f});
// matrix of doubles
writeArrayValuesToStream<double>(m_bytes, {1.0, 2.0, 3.0, 4.0, 5.0, 6.0});
}

template <typename T> void writeSingleValueToStream(std::ostream &, T value) {
Expand Down

0 comments on commit d613c53

Please sign in to comment.