Skip to content

Commit

Permalink
#329: Implement stream serializer
Browse files Browse the repository at this point in the history
  • Loading branch information
Matthew-Whitlock committed Mar 21, 2024
1 parent 27d3803 commit 78bba6e
Show file tree
Hide file tree
Showing 5 changed files with 357 additions and 1 deletion.
44 changes: 44 additions & 0 deletions src/checkpoint/checkpoint_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,50 @@ std::unique_ptr<T> deserializeFromFile(std::string const& file);
template <typename T>
void deserializeInPlaceFromFile(std::string const& file, T* buf);

/**
* \brief Serialize \c T to a stream
*
* Byte-serializes \c T to stream. Handling of any errors during writing
* to the stream will be handled by the stream itself, e.g. any exceptions
* or status bits to check will depend on stream type.
*
* \param[in] target the \c T to serialize
* \param[in] stream to serialize into, with tellp and write functions.
*/
template <typename T, typename StreamT>
void serializeToStream(T& target, StreamT& stream);

/**
* \brief De-serialize and reify \c T from a stream
*
* De-serializes an object recursively by first invoking the reconstruction
* strategy and then \c serialize functions/methods recursively to rebuild the
* state of the object as serialized. During reconstruction, based on trait
* detection, \c T will either be default constructed or reconstructed based on
* a user-defined reconstruct method.
*
* \param[in] stream the stream to read with bytes for \c T, with tellg and read functions
*
* \return unique pointer to the new object \c T
*/
template <typename T, typename StreamT>
std::unique_ptr<T> deserializeFromStream(StreamT& stream);

/**
* \brief De-serialize and reify \c T from a stream in place on an existing
* pointer to \c T
*
* De-serializes an object recursively by invoking the \c serialize
* functions/methods recursively to rebuild the state of the object as
* serialized.
*
* \param[in] stream the stream to read with bytes for \c T, with tellg and read functions
* \param[in] t a valid, constructed \c T to deserialize into
*/
template <typename T, typename StreamT>
void deserializeInPlaceFromStream(StreamT& stream, T* buf);


} /* end namespace checkpoint */

#endif /*INCLUDED_CHECKPOINT_CHECKPOINT_API_H*/
25 changes: 25 additions & 0 deletions src/checkpoint/checkpoint_api.impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,31 @@ void deserializeInPlaceFromFile(std::string const& file, T* t) {
);
}

template <typename T, typename StreamT>
void serializeToStream(T& target, StreamT& stream) {
auto len = getSize<T>(target);
dispatch::Standard::pack<T, StreamPacker<StreamT>>(
target, len, stream
);
}

template <typename T, typename StreamT>
std::unique_ptr<T> deserializeFromStream(StreamT& stream) {
auto mem = dispatch::Standard::allocate<T>();
T* t_buf = dispatch::Standard::construct<T>(mem);
auto t = dispatch::Standard::unpack<T, StreamUnpacker<StreamT>>(
t_buf, stream
);
return std::unique_ptr<T>(t);
}

template <typename T, typename StreamT>
void deserializeInPlaceFromStream(StreamT& stream, T* t) {
dispatch::Standard::unpack<T, StreamUnpacker<StreamT>>(
t, stream
);
}

} /* end namespace checkpoint */

#endif /*INCLUDED_CHECKPOINT_CHECKPOINT_API_IMPL_H*/
5 changes: 4 additions & 1 deletion src/checkpoint/serializers/serializers_headers.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "checkpoint/serializers/sizer.h"
#include "checkpoint/serializers/packer.h"
#include "checkpoint/serializers/unpacker.h"
#include "checkpoint/serializers/stream_serializer.h"

#define checkpoint_serializer_variadic_args() \
checkpoint::Footprinter, \
Expand All @@ -58,6 +59,8 @@
checkpoint::PackerIO, \
checkpoint::Unpacker, \
checkpoint::UnpackerIO, \
checkpoint::Sizer \
checkpoint::Sizer, \
checkpoint::StreamPacker<>, \
checkpoint::StreamUnpacker<> \

#endif /*INCLUDED_CHECKPOINT_SERIALIZERS_SERIALIZERS_HEADERS_H*/
99 changes: 99 additions & 0 deletions src/checkpoint/serializers/stream_serializer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
//@HEADER
// *****************************************************************************
//
// stream_serializer.h
// DARMA/checkpoint => Serialization Library
//
// Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC
// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Questions? Contact darma@sandia.gov
//
// *****************************************************************************
//@HEADER
*/

#if !defined INCLUDED_CHECKPOINT_SERIALIZERS_STREAM_SERIALIZER_H
#define INCLUDED_CHECKPOINT_SERIALIZERS_STREAM_SERIALIZER_H

#include "checkpoint/common.h"
#include "checkpoint/serializers/base_serializer.h"
#include <ostream>
#include <istream>

namespace checkpoint {

template<typename StreamT = std::ostream>
struct StreamPacker : BaseSerializer {
StreamPacker(SerialSizeType size, StreamT& m_stream)
: BaseSerializer(ModeType::Packing), stream(m_stream) {
//Nothing to do with the size.
//Pre-allocating a buffer for the stream has more problems than solutions.
}

void contiguousBytes(void* ptr, SerialSizeType size, SerialSizeType num_elms) {
stream.write(static_cast<char*>(ptr), size*num_elms);
n_bytes += size*num_elms;
}

SerialSizeType usedBufferSize() {
return n_bytes;
}

private:
StreamT& stream;
SerialSizeType n_bytes = 0;
};

template<typename StreamT = std::istream>
struct StreamUnpacker : BaseSerializer {
StreamUnpacker(StreamT& m_stream)
: BaseSerializer(ModeType::Unpacking), stream(m_stream) { }

void contiguousBytes(void* ptr, SerialSizeType size, SerialSizeType num_elms) {
stream.read(static_cast<char*>(ptr), size*num_elms);
if(static_cast<SerialSizeType>(stream.gcount()) != size*num_elms)
throw std::runtime_error("Stream unable to read required number of bytes!");
n_bytes += size*num_elms;
}

SerialSizeType usedBufferSize() {
return n_bytes;
}

private:
StreamT& stream;
SerialSizeType n_bytes = 0;
};

} /* end namespace checkpoint */

#endif /*INCLUDED_CHECKPOINT_SERIALIZERS_STREAM_SERIALIZER_H*/
185 changes: 185 additions & 0 deletions tests/unit/test_serialize_stream.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/*
//@HEADER
// *****************************************************************************
//
// test_serialize_stream.cc
// DARMA/checkpoint => Serialization Library
//
// Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC
// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Questions? Contact darma@sandia.gov
//
// *****************************************************************************
//@HEADER
*/

#include "test_harness.h"

#include <checkpoint/checkpoint.h>
#include <fstream>

#include <gtest/gtest.h>

#include <vector>
#include <cstdio>

namespace checkpoint { namespace tests { namespace unit {

template <typename T>
struct TestSerializeStream : TestHarness { };
template <typename T>
struct TestSerializeStreamInPlace : TestHarness { };

TYPED_TEST_CASE_P(TestSerializeStream);
TYPED_TEST_CASE_P(TestSerializeStreamInPlace);

static constexpr int const u_val = 934;

struct UserObjectA {
UserObjectA() = default;
explicit UserObjectA(int in_u) : u_(in_u) { }

void check() {
EXPECT_EQ(u_, u_val);
}

template <typename SerializerT>
void serialize(SerializerT& s) {
s | u_;
}

int u_;
};

struct UserObjectB {
UserObjectB() = default;
explicit UserObjectB(int in_u) : len_(in_u) {
u_.resize(len_);
for (int i = 0; i < len_; i++) {
u_[i] = u_val+i;
}
}

void check() {
EXPECT_EQ(u_.size(), static_cast<std::size_t>(len_));
int i = 0;
for (auto&& elm : u_) {
EXPECT_EQ(elm, u_val+i++);
}
}

template <typename SerializerT>
void serialize(SerializerT& s) {
s | u_;
s | len_;
}

std::vector<double> u_;
int len_ = 0;
};

struct UserObjectC {
UserObjectC() = default;
explicit UserObjectC(int in_u) : u_(std::to_string(in_u)) { }

void check() {
EXPECT_EQ(u_, std::to_string(u_val));
}

template <typename SerializerT>
void serialize(SerializerT& s) {
s | u_;
}

std::string u_ = {};
};

/*
* General test of serialization/deserialization for input object types
*/

TYPED_TEST_P(TestSerializeStream, test_serialize_stream_multi) {
using TestType = TypeParam;

TestType in(u_val);
in.check();

auto len = checkpoint::getSize(in);
printf("len=%lu\n", len);

{
std::ofstream ostream("hello-stream.txt", std::ios::binary | std::ios::out | std::ios::trunc);
checkpoint::serializeToStream(in, ostream);
}

{
std::ifstream istream("hello-stream.txt", std::ios::binary | std::ios::in);
auto out = checkpoint::deserializeFromStream<TestType>(istream);
out->check();
}
}

TYPED_TEST_P(TestSerializeStreamInPlace, test_serialize_stream_multi_in_place) {
using TestType = TypeParam;

TestType in(u_val);
in.check();

auto len = checkpoint::getSize(in);
printf("len=%lu\n", len);

{
std::ofstream ostream("hello-stream.txt", std::ios::binary | std::ios::out | std::ios::trunc);
checkpoint::serializeToStream(in, ostream);
}
{
TestType out{};

std::ifstream istream("hello-stream.txt", std::ios::binary | std::ios::in);
checkpoint::deserializeInPlaceFromStream<TestType>(istream, &out);

out.check();
}
}

using ConstructTypes = ::testing::Types<
UserObjectA,
UserObjectB,
UserObjectC
>;

REGISTER_TYPED_TEST_CASE_P(TestSerializeStream, test_serialize_stream_multi);
REGISTER_TYPED_TEST_CASE_P(TestSerializeStreamInPlace, test_serialize_stream_multi_in_place);

INSTANTIATE_TYPED_TEST_CASE_P(test_file, TestSerializeStream, ConstructTypes, );
INSTANTIATE_TYPED_TEST_CASE_P(test_file_in_place, TestSerializeStreamInPlace, ConstructTypes, );

}}} // end namespace checkpoint::tests::unit

0 comments on commit 78bba6e

Please sign in to comment.