Skip to content

Commit

Permalink
Clean up handling of temporary files in test suite (#652)
Browse files Browse the repository at this point in the history
* Don't write `test.aig` from `write_aiger` test

I assume this was added for debugging purposes at some point.
The test doesn't need it and the current working directory might not be
writable.

* Set the current working directory to a temporary directory when running tests

The incoming current working directory may not be writable.

* Propagate I/O errors when (de)serializing

This is especially important when deserializing. If we don't catch
errors after reading `size`, we may use its uninitialized value as a
loop bound, which is very bad.

* Add tests for I/O error propagation
  • Loading branch information
rocallahan authored Jul 15, 2024
1 parent da7c921 commit b78357b
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 34 deletions.
103 changes: 83 additions & 20 deletions include/mockturtle/io/serialize.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

#include "../networks/aig.hpp"
#include <fstream>
#include <optional>
#include <parallel_hashmap/phmap_dump.h>

namespace mockturtle
Expand Down Expand Up @@ -93,7 +94,10 @@ struct serializer
bool operator()( phmap::BinaryOutputArchive& os, regular_node<Fanin, Size, PointerFieldSize> const& n ) const
{
uint64_t size = n.children.size();
os.dump( (char*)&size, sizeof( uint64_t ) );
if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) )
{
return false;
}

for ( const auto& c : n.children )
{
Expand All @@ -105,7 +109,10 @@ struct serializer
}

size = n.data.size();
os.dump( (char*)&size, sizeof( uint64_t ) );
if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) )
{
return false;
}
for ( const auto& d : n.data )
{
bool result = this->operator()( os, d );
Expand All @@ -122,7 +129,11 @@ struct serializer
bool operator()( phmap::BinaryInputArchive& ar_input, const regular_node<Fanin, Size, PointerFieldSize>* n ) const
{
uint64_t size;
ar_input.load( (char*)&size, sizeof( uint64_t ) );
if ( !ar_input.load( (char*)&size, sizeof( uint64_t ) ) )
{
return false;
}

for ( uint64_t i = 0; i < size; ++i )
{
pointer_type ptr;
Expand Down Expand Up @@ -163,7 +174,10 @@ struct serializer
{
/* nodes */
uint64_t size = storage.nodes.size();
os.dump( (char*)&size, sizeof( uint64_t ) );
if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) )
{
return false;
}
for ( const auto& n : storage.nodes )
{
if ( !this->operator()( os, n ) )
Expand All @@ -174,7 +188,10 @@ struct serializer

/* inputs */
size = storage.inputs.size();
os.dump( (char*)&size, sizeof( uint64_t ) );
if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) )
{
return false;
}
for ( const auto& i : storage.inputs )
{
if ( !this->operator()( os, i ) )
Expand All @@ -185,7 +202,10 @@ struct serializer

/* outputs */
size = storage.outputs.size();
os.dump( (char*)&size, sizeof( uint64_t ) );
if ( !os.dump( (char*)&size, sizeof( uint64_t ) ) )
{
return false;
}
for ( const auto& o : storage.outputs )
{
if ( !this->operator()( os, o ) )
Expand All @@ -200,7 +220,10 @@ struct serializer
return false;
}

os.dump( (char*)&storage.trav_id, sizeof( uint32_t ) );
if ( !os.dump( (char*)&storage.trav_id, sizeof( uint32_t ) ) )
{
return false;
}

return true;
}
Expand All @@ -209,7 +232,10 @@ struct serializer
{
/* nodes */
uint64_t size;
ar_input.load( (char*)&size, sizeof( uint64_t ) );
if ( !ar_input.load( (char*)&size, sizeof( uint64_t ) ) )
{
return false;
}
for ( uint64_t i = 0; i < size; ++i )
{
node_type n;
Expand All @@ -221,16 +247,25 @@ struct serializer
}

/* inputs */
ar_input.load( (char*)&size, sizeof( uint64_t ) );
if ( !ar_input.load( (char*)&size, sizeof( uint64_t ) ) )
{
return false;
}
for ( uint64_t i = 0; i < size; ++i )
{
uint64_t value;
ar_input.load( (char*)&value, sizeof( uint64_t ) );
if ( !ar_input.load( (char*)&value, sizeof( uint64_t ) ) )
{
return false;
}
storage->inputs.push_back( value );
}

/* outputs */
ar_input.load( (char*)&size, sizeof( uint64_t ) );
if ( !ar_input.load( (char*)&size, sizeof( uint64_t ) ) )
{
return false;
}
for ( uint64_t i = 0; i < size; ++i )
{
pointer_type ptr;
Expand All @@ -247,23 +282,36 @@ struct serializer
return false;
}

ar_input.load( (char*)&storage->trav_id, sizeof( uint32_t ) );
if ( !ar_input.load( (char*)&storage->trav_id, sizeof( uint32_t ) ) )
{
return false;
}

return true;
}
}; /* struct serializer */

} /* namespace detail */

/*! \brief Serializes a combinational AIG network to a archive, returning false on failure
*
* \param aig Combinational AIG network
* \param os Output archive
*/
inline bool serialize_network_fallible( aig_network const& aig, phmap::BinaryOutputArchive& os )
{
detail::serializer _serializer;
return _serializer( os, *aig._storage );
}

/*! \brief Serializes a combinational AIG network to a archive
*
* \param aig Combinational AIG network
* \param os Output archive
*/
inline void serialize_network( aig_network const& aig, phmap::BinaryOutputArchive& os )
{
detail::serializer _serializer;
bool const okay = _serializer( os, *aig._storage );
bool const okay = serialize_network_fallible( aig, os );
(void)okay;
assert( okay && "failed to serialize the network onto stream" );
}
Expand All @@ -279,12 +327,12 @@ inline void serialize_network( aig_network const& aig, std::string const& filena
serialize_network( aig, ar_out );
}

/*! \brief Deserializes a combinational AIG network from a input archive
/*! \brief Deserializes a combinational AIG network from a input archive, returning nullopt on failure
*
* \param ar_input Input archive
* \return Deserialized AIG network
*/
inline aig_network deserialize_network( phmap::BinaryInputArchive& ar_input )
inline std::optional<aig_network> deserialize_network_fallible( phmap::BinaryInputArchive& ar_input )
{
detail::serializer _serializer;
auto storage = std::make_shared<aig_storage>();
Expand All @@ -293,10 +341,25 @@ inline aig_network deserialize_network( phmap::BinaryInputArchive& ar_input )
storage->outputs.clear();
storage->hash.clear();

bool const okay = _serializer( ar_input, storage.get() );
(void)okay;
assert( okay && "failed to deserialize the network onto stream" );
return aig_network{ storage };
if ( _serializer( ar_input, storage.get() ) )
{
return aig_network{ storage };
}

return std::nullopt;
}

/*! \brief Deserializes a combinational AIG network from a input archive
*
* \param ar_input Input archive
* \return Deserialized AIG network
*/
inline aig_network deserialize_network( phmap::BinaryInputArchive& ar_input )
{
auto result = deserialize_network_fallible( ar_input );
(void)result.has_value();
assert( result.has_value() && "failed to deserialize the network onto stream" );
return *result;
}

/*! \brief Deserializes a combinational AIG network from a file
Expand Down
36 changes: 28 additions & 8 deletions lib/parallel_hashmap/parallel_hashmap/phmap_dump.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include <iostream>
#include <fstream>
#include <limits>
#include <sstream>
#include "phmap.h"
namespace phmap
Expand Down Expand Up @@ -188,47 +189,66 @@ bool parallel_hash_set<N, RefSet, Mtx_, Policy, Hash, Eq, Alloc>::load(InputArch
// ------------------------------------------------------------------------
class BinaryOutputArchive {
public:
BinaryOutputArchive(const char *file_path) {
BinaryOutputArchive(const char *file_path,
size_t bytes_remaining = std::numeric_limits<size_t>::max())
: bytes_remaining_(bytes_remaining) {
ofs_.open(file_path, std::ios_base::binary);
}

bool dump(const char *p, size_t sz) {
if ( sz > bytes_remaining_ ) {
bytes_remaining_ = 0;
return false;
}
bytes_remaining_ -= sz;
ofs_.write(p, sz);
return true;
return ofs_.good();
}

template<typename V>
typename std::enable_if<type_traits_internal::IsTriviallyCopyable<V>::value, bool>::type
dump(const V& v) {
ofs_.write(reinterpret_cast<const char *>(&v), sizeof(V));
return true;
return dump(reinterpret_cast<const char *>(&v), sizeof(V));
}

bool close() {
ofs_.close();
return ofs_.good();
}

private:
std::ofstream ofs_;
size_t bytes_remaining_;
};


class BinaryInputArchive {
public:
BinaryInputArchive(const char * file_path) {
BinaryInputArchive(const char * file_path,
size_t bytes_remaining = std::numeric_limits<size_t>::max())
: bytes_remaining_(bytes_remaining) {
ifs_.open(file_path, std::ios_base::binary);
}

bool load(char* p, size_t sz) {
if ( sz > bytes_remaining_ ) {
bytes_remaining_ = 0;
return false;
}
bytes_remaining_ -= sz;
ifs_.read(p, sz);
return true;
return ifs_.good();
}

template<typename V>
typename std::enable_if<type_traits_internal::IsTriviallyCopyable<V>::value, bool>::type
load(V* v) {
ifs_.read(reinterpret_cast<char *>(v), sizeof(V));
return true;
return load(reinterpret_cast<char *>(v), sizeof(V));
}

private:
std::ifstream ifs_;
size_t bytes_remaining_;
};

} // namespace phmap
Expand Down
Loading

0 comments on commit b78357b

Please sign in to comment.