Skip to content

Commit

Permalink
default to use serialize and parse from custom class and override << …
Browse files Browse the repository at this point in the history
…and >> (#31)
  • Loading branch information
jl2922 authored May 3, 2018
1 parent 68757b0 commit 8cbb036
Show file tree
Hide file tree
Showing 51 changed files with 380 additions and 399 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "googletest"]
path = googletest
url = https://github.com/google/googletest.git
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ OBJS := $(SRCS:$(SRC_DIR)/%.cc=$(BUILD_DIR)/%.o)
TEST_OBJS := $(TESTS:$(SRC_DIR)/%.cc=$(BUILD_DIR)/%.o)

# Test related.
GTEST_DIR := gtest/googletest
GMOCK_DIR := gtest/googlemock
GTEST_DIR := googletest/googletest
GMOCK_DIR := googletest/googlemock
GTEST_ALL_SRC := ${GTEST_DIR}/src/gtest-all.cc
GMOCK_ALL_SRC := ${GMOCK_DIR}/src/gmock-all.cc
TEST_MAIN_SRC := ${GMOCK_DIR}/src/gmock_main.cc
Expand Down
61 changes: 25 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ For primitive types and most STL containers, serialization requires only one lin
int main() {
std::vector<int> data({22, 333, -4444});

std::string serialized = hps::serialize_to_string(data);
auto parsed = hps::parse_from_string<std::vector<int>>(serialized);
std::string serialized = hps::to_string(data);
auto parsed = hps::from_string<std::vector<int>>(serialized);

assert(parsed == data);

Expand All @@ -52,19 +52,21 @@ Then we can send the string over the network in MPI for example
```c++
MPI_Send(serialized.c_str(), serialized.size(), MPI_CHAR, 0, 0, MPI_COMM_WORLD);
```
There are also the `serialize_to_stream` and `parse_from_stream` functions for writing the data to or reading it from file streams.
There are also the `to_stream` and `from_stream` functions for writing the data to or reading it from file streams.
For example
```c++
std::ofstream out_file("data.log", std::ofstream::binary);
hps::serialize_to_stream(data, out_file);
hps::to_stream(data, out_file);
std::ifstream in_file("data.log", std::ifstream::binary);
auto parsed = hps::parse_from_stream<std::vector<int>>(in_file);
auto parsed = hps::from_stream<std::vector<int>>(in_file);
```
The bottom of this document contains all the APIs that HPS provides.

We can also extend HPS to support custom types.
HPS internally uses static polymorphism on the class `Serializer<DataType, BufferType>` to support different types.
All we need to do is to specialize the `Serializer` for the new type, and HPS will support it, together with any combination of this type with STL containers and other specialized types.
`Serializer<DataType, BufferType>` will call the `serialize` and `parse` method of the corresponding type by default.
All we need to do is provide the `serialize` and `parse` method or specialize the `Serializer` for the new type, and HPS will support it, together with any combination of this type with STL containers and other specialized types.
We recommend providing the `serialize` and `parse` method whenever you have control over the structure of the new type.

The following example shows the serialization of a typical quantum system object consisting of several electrons, some of which are excited from orbitals `orbs_from` to `orbs_to`.

Expand All @@ -75,42 +77,29 @@ The following example shows the serialization of a typical quantum system object

class QuantumState {
public:
uint16_t n_elecs;
std::unordered_set<uint16_t> orbs_from;
std::unordered_set<uint16_t> orbs_to;
unsigned n_elecs;
std::unordered_set<unsigned> orbs_from;
std::unordered_set<unsigned> orbs_to;

template <class B>
void serialize(hps::OutputBuffer<B>& buf) const {
hps::Serializer<uint16_t, B>::serialize(n_elecs, buf);
hps::Serializer<std::unordered_set<uint16_t>, B>::serialize(orbs_from, buf);
hps::Serializer<std::unordered_set<uint16_t>, B>::serialize(orbs_to, buf);
void serialize(B& buf) const {
buf << n_elecs << orbs_from << orbs_to;
}

template <class B>
void parse(hps::InputBuffer<B>& buf) {
hps::Serializer<uint16_t, B>::parse(n_elecs, buf);
hps::Serializer<std::unordered_set<uint16_t>, B>::parse(orbs_from, buf);
hps::Serializer<std::unordered_set<uint16_t>, B>::parse(orbs_to, buf);
void parse(B& buf) {
buf >> n_elecs >> orbs_from >> orbs_to;
}
};

namespace hps {
template <class B>
class Serializer<QuantumState, B> {
public:
static void serialize(const QuantumState& qs, OutputBuffer<B>& buf) { qs.serialize(buf); }
static void parse(QuantumState& qs, InputBuffer<B>& buf) { qs.parse(buf); }
};
} // namespace hps

int main() {
QuantumState qs;

qs.n_elecs = 33;
qs.orbs_from.insert({11, 22});
qs.orbs_to.insert({44, 66});

std::string serialized = hps::serialize_to_string(qs);
std::string serialized = hps::to_string(qs);

std::cout << "size (B): " << serialized.size() << std::endl;
// size (B): 7
Expand Down Expand Up @@ -165,43 +154,43 @@ Note: fixed cost includes the estimated amount of lines of commands needed for a
```c++
// Serialize data t to an STL ostream.
void serialize_to_stream(const T& t, std::ostream& stream);
void to_stream(const T& t, std::ostream& stream);
```
```c++
// Parse from an STL istream and save to the data t passed in.
// Recommended for repeated use inside a loop.
void parse_from_stream(T& t, std::istream& stream);
void from_stream(T& t, std::istream& stream);
```
```c++
// Parse from an STL istream and return the data.
T parse_from_stream<T>(std::istream& stream);
T from_stream<T>(std::istream& stream);
```
```c++
// Serialize data t to the STL string passed in.
// Recommended for repeated use inside a loop.
void serialize_to_string(const T& t, std::string& str);
void to_string(const T& t, std::string& str);
```
```c++
// Serialize data t to an STL string and return it.
std::string serialize_to_string(const T& t);
std::string to_string(const T& t);
```
```c++
// Parse from an STL string and save to the data t passed in.
// Recommended for repeated use inside a loop.
void parse_from_string(T& t, const std::string& str);
void from_string(T& t, const std::string& str);
```
```c++
// Parse from an STL string and return the data.
T parse_from_string<T>(const std::string& str);
T from_string<T>(const std::string& str);
```
```c++
// Parse from a char array and save to the data t passed in.
// Recommended for repeated use inside a loop.
void parse_from_char_array(T& t, const char* arr);
void from_char_array(T& t, const char* arr);
```
```c++
// Parse from a char array and return the data.
T parse_from_char_array<T>(const char* arr);
T from_char_array<T>(const char* arr);
```

HPS supports the following types and any combinations of them out of the box:
Expand Down
4 changes: 2 additions & 2 deletions benchmark.mk
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ OBJS := $(SRCS:$(SRC_DIR)/%.cc=$(BUILD_DIR)/%.o)
TEST_OBJS := $(TESTS:$(SRC_DIR)/%.cc=$(BUILD_DIR)/%.o)

# Test related.
GTEST_DIR := gtest/googletest
GMOCK_DIR := gtest/googlemock
GTEST_DIR := googletest/googletest
GMOCK_DIR := googletest/googlemock
GTEST_ALL_SRC := ${GTEST_DIR}/src/gtest-all.cc
GMOCK_ALL_SRC := ${GMOCK_DIR}/src/gmock-all.cc
TEST_MAIN_SRC := ${GMOCK_DIR}/src/gmock_main.cc
Expand Down
8 changes: 0 additions & 8 deletions ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ set -x
export TOOLS_DIR=$TRAVIS_BUILD_DIR/tools
echo $TOOLS_DIR

# Install Google Test.
echo "Downloading Google Test"
wget -O release-1.8.0.tar.gz https://github.com/google/googletest/archive/release-1.8.0.tar.gz
tar xzf release-1.8.0.tar.gz
rm release-1.8.0.tar.gz
mv googletest-release-1.8.0 gtest
echo "Completed"

# Install or Load Protocol Buffers.
if [ -f "$TOOLS_DIR/protobuf/bin/protoc" ]; then
echo "Found cached Protocol Buffers"
Expand Down
4 changes: 2 additions & 2 deletions example/basic.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
int main() {
std::vector<int> data({22, 333, -4444});

std::string serialized = hps::serialize_to_string(data);
auto parsed = hps::parse_from_string<std::vector<int>>(serialized);
std::string serialized = hps::to_string(data);
auto parsed = hps::from_string<std::vector<int>>(serialized);

assert(parsed == data);

Expand Down
30 changes: 12 additions & 18 deletions example/custom_type.cc
Original file line number Diff line number Diff line change
@@ -1,30 +1,24 @@
#include <cassert>
#include <iostream>
#include <unordered_set>
#include "../src/hps.h"

class QuantumState {
public:
uint16_t n_elecs;
std::unordered_set<uint16_t> orbs_from;
std::unordered_set<uint16_t> orbs_to;
};
unsigned n_elecs;
std::unordered_set<unsigned> orbs_from;
std::unordered_set<unsigned> orbs_to;

namespace hps {
template <class B>
class Serializer<QuantumState, B> {
public:
static void serialize(const QuantumState& qs, OutputBuffer<B>& ob) {
Serializer<uint16_t, B>::serialize(qs.n_elecs, ob);
Serializer<std::unordered_set<uint16_t>, B>::serialize(qs.orbs_from, ob);
Serializer<std::unordered_set<uint16_t>, B>::serialize(qs.orbs_to, ob);
template <class B>
void serialize(B& buf) const {
buf << n_elecs << orbs_from << orbs_to;
}
static void parse(QuantumState& qs, InputBuffer<B>& ib) {
Serializer<uint16_t, B>::parse(qs.n_elecs, ib);
Serializer<std::unordered_set<uint16_t>, B>::parse(qs.orbs_from, ib);
Serializer<std::unordered_set<uint16_t>, B>::parse(qs.orbs_to, ib);

template <class B>
void parse(B& buf) {
buf >> n_elecs >> orbs_from >> orbs_to;
}
};
} // namespace hps

int main() {
QuantumState qs;
Expand All @@ -33,7 +27,7 @@ int main() {
qs.orbs_from.insert({11, 22});
qs.orbs_to.insert({44, 66});

std::string serialized = hps::serialize_to_string(qs);
std::string serialized = hps::to_string(qs);

std::cout << "size (B): " << serialized.size() << std::endl;
// size (B): 7
Expand Down
4 changes: 2 additions & 2 deletions example/file_stream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ int main() {
std::ofstream out_file("data.log", std::ofstream::binary);
std::ifstream in_file("data.log", std::ifstream::binary);

hps::serialize_to_stream(data, out_file);
hps::to_stream(data, out_file);
out_file.close();

in_file.seekg(0, in_file.end);
size_t serialized_size = in_file.tellg();
in_file.seekg(0, in_file.beg);
auto parsed = hps::parse_from_stream<std::vector<int>>(in_file);
auto parsed = hps::from_stream<std::vector<int>>(in_file);

assert(parsed == data);

Expand Down
1 change: 1 addition & 0 deletions googletest
Submodule googletest added at ec44c6
4 changes: 2 additions & 2 deletions src/basic_type/float_serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ namespace hps {
template <class T, class B>
class Serializer<T, B, typename std::enable_if<std::is_floating_point<T>::value, void>::type> {
public:
static void serialize(const T& num, OutputBuffer<B>& ob) {
static void serialize(const T& num, B& ob) {
const char* num_ptr = reinterpret_cast<const char*>(&num);
ob.write(num_ptr, sizeof(num));
}

static void parse(T& num, InputBuffer<B>& ib) {
static void parse(T& num, B& ib) {
char* num_ptr = reinterpret_cast<char*>(&num);
ib.read(num_ptr, sizeof(num));
}
Expand Down
37 changes: 19 additions & 18 deletions src/basic_type/float_serializer_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,61 @@
#include <gtest/gtest.h>
#include <iostream>
#include <limits>
#include "../buffer/buffer.h"

TEST(FloatSerializerTest, Zero) {
const double input = 0;
std::stringstream ss;
hps::OutputBuffer<hps::Stream> ob(ss);
hps::Serializer<double, hps::Stream>::serialize(input, ob);
hps::StreamOutputBuffer ob(ss);
hps::Serializer<double, hps::StreamOutputBuffer>::serialize(input, ob);
ob.flush();

hps::InputBuffer<hps::Stream> ib(ss);
hps::StreamInputBuffer ib(ss);
double output;
hps::Serializer<double, hps::Stream>::parse(output, ib);
hps::Serializer<double, hps::StreamInputBuffer>::parse(output, ib);
EXPECT_EQ(input, output);
}

TEST(FloatSerializerTest, Float) {
const float input = 3.3;
std::stringstream ss;
hps::OutputBuffer<hps::Stream> ob(ss);
hps::Serializer<float, hps::Stream>::serialize(input, ob);
hps::StreamOutputBuffer ob(ss);
hps::Serializer<float, hps::StreamOutputBuffer>::serialize(input, ob);
ob.flush();

hps::InputBuffer<hps::Stream> ib(ss);
hps::StreamInputBuffer ib(ss);
float output;
hps::Serializer<float, hps::Stream>::parse(output, ib);
hps::Serializer<float, hps::StreamInputBuffer>::parse(output, ib);
EXPECT_EQ(input, output);
}

TEST(FloatSerializerTest, Double) {
const double input = 3.3;
std::stringstream ss;
hps::OutputBuffer<hps::Stream> ob(ss);
hps::Serializer<double, hps::Stream>::serialize(input, ob);
hps::StreamOutputBuffer ob(ss);
hps::Serializer<double, hps::StreamOutputBuffer>::serialize(input, ob);
ob.flush();

hps::InputBuffer<hps::Stream> ib(ss);
hps::StreamInputBuffer ib(ss);
double output;
hps::Serializer<double, hps::Stream>::parse(output, ib);
hps::Serializer<double, hps::StreamInputBuffer>::parse(output, ib);
EXPECT_EQ(input, output);
}

TEST(FloatSerializerTest, TwoDoubles) {
const double input1 = 3.2;
const double input2 = -4.4;
std::stringstream ss;
hps::OutputBuffer<hps::Stream> ob(ss);
hps::Serializer<double, hps::Stream>::serialize(input1, ob);
hps::Serializer<double, hps::Stream>::serialize(input2, ob);
hps::StreamOutputBuffer ob(ss);
hps::Serializer<double, hps::StreamOutputBuffer>::serialize(input1, ob);
hps::Serializer<double, hps::StreamOutputBuffer>::serialize(input2, ob);
ob.flush();

hps::InputBuffer<hps::Stream> ib(ss);
hps::StreamInputBuffer ib(ss);
double output1;
double output2;
hps::Serializer<double, hps::Stream>::parse(output1, ib);
hps::Serializer<double, hps::Stream>::parse(output2, ib);
hps::Serializer<double, hps::StreamInputBuffer>::parse(output1, ib);
hps::Serializer<double, hps::StreamInputBuffer>::parse(output2, ib);
EXPECT_EQ(input1, output1);
EXPECT_EQ(input2, output2);
}
4 changes: 2 additions & 2 deletions src/basic_type/int_serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ class Serializer<
B,
typename std::enable_if<std::is_signed<T>::value && std::is_integral<T>::value, void>::type> {
public:
static void serialize(const T& num, OutputBuffer<B>& ob) {
static void serialize(const T& num, B& ob) {
const size_t n_bits = sizeof(num) * 8;
using UT = typename std::make_unsigned<T>::type;
UT zigzaged_num = (num << 1) ^ (num >> (n_bits - 1));
Serializer<UT, B>::serialize(zigzaged_num, ob);
}

static void parse(T& num, InputBuffer<B>& ib) {
static void parse(T& num, B& ib) {
using UT = typename std::make_unsigned<T>::type;
UT zigzaged_num;
Serializer<UT, B>::parse(zigzaged_num, ib);
Expand Down
Loading

0 comments on commit 8cbb036

Please sign in to comment.