diff --git a/src/checkpoint/checkpoint_api.h b/src/checkpoint/checkpoint_api.h index 4198d587..0234bd9a 100644 --- a/src/checkpoint/checkpoint_api.h +++ b/src/checkpoint/checkpoint_api.h @@ -217,6 +217,50 @@ std::unique_ptr deserializeFromFile(std::string const& file); template 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 +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 +std::unique_ptr 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 +void deserializeInPlaceFromStream(StreamT& stream, T* buf); + + } /* end namespace checkpoint */ #endif /*INCLUDED_CHECKPOINT_CHECKPOINT_API_H*/ diff --git a/src/checkpoint/checkpoint_api.impl.h b/src/checkpoint/checkpoint_api.impl.h index e649c5dd..0386a88a 100644 --- a/src/checkpoint/checkpoint_api.impl.h +++ b/src/checkpoint/checkpoint_api.impl.h @@ -123,6 +123,31 @@ void deserializeInPlaceFromFile(std::string const& file, T* t) { ); } +template +void serializeToStream(T& target, StreamT& stream) { + auto len = getSize(target); + dispatch::Standard::pack>( + target, len, stream + ); +} + +template +std::unique_ptr deserializeFromStream(StreamT& stream) { + auto mem = dispatch::Standard::allocate(); + T* t_buf = dispatch::Standard::construct(mem); + auto t = dispatch::Standard::unpack>( + t_buf, stream + ); + return std::unique_ptr(t); +} + +template +void deserializeInPlaceFromStream(StreamT& stream, T* t) { + dispatch::Standard::unpack>( + t, stream + ); +} + } /* end namespace checkpoint */ #endif /*INCLUDED_CHECKPOINT_CHECKPOINT_API_IMPL_H*/ diff --git a/src/checkpoint/serializers/serializers_headers.h b/src/checkpoint/serializers/serializers_headers.h index bc6fda64..06e6ac66 100644 --- a/src/checkpoint/serializers/serializers_headers.h +++ b/src/checkpoint/serializers/serializers_headers.h @@ -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, \ @@ -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*/ diff --git a/src/checkpoint/serializers/stream_serializer.h b/src/checkpoint/serializers/stream_serializer.h new file mode 100644 index 00000000..19bf811c --- /dev/null +++ b/src/checkpoint/serializers/stream_serializer.h @@ -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 +#include + +namespace checkpoint { + +template +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(ptr), size*num_elms); + n_bytes += size*num_elms; + } + + SerialSizeType usedBufferSize() { + return n_bytes; + } + +private: + StreamT& stream; + SerialSizeType n_bytes = 0; +}; + +template +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(ptr), size*num_elms); + if(static_cast(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*/ diff --git a/tests/unit/test_serialize_stream.cc b/tests/unit/test_serialize_stream.cc new file mode 100644 index 00000000..aa7c0e42 --- /dev/null +++ b/tests/unit/test_serialize_stream.cc @@ -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 +#include + +#include + +#include +#include + +namespace checkpoint { namespace tests { namespace unit { + +template +struct TestSerializeStream : TestHarness { }; +template +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 + 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(len_)); + int i = 0; + for (auto&& elm : u_) { + EXPECT_EQ(elm, u_val+i++); + } + } + + template + void serialize(SerializerT& s) { + s | u_; + s | len_; + } + + std::vector 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 + 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(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(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