Skip to content

Commit

Permalink
Merge pull request #1104 from AntelopeIO/fix_unpack
Browse files Browse the repository at this point in the history
Fix upacking() issues for std::optional, std::shared_ptr, std::set, and std::list
  • Loading branch information
linh2931 authored Jan 16, 2025
2 parents 0e60eac + 2d22036 commit b045377
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 1 deletion.
5 changes: 4 additions & 1 deletion libraries/libfc/include/fc/io/raw.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ namespace fc {
auto tmp = std::make_shared<std::remove_const_t<T>>();
fc::raw::unpack( s, *tmp );
v = std::move(tmp);
}
} else { v.reset(); }
} FC_RETHROW_EXCEPTIONS( warn, "std::shared_ptr<T>", ("type",fc::get_typename<T>::name()) ) }

template<typename Stream> inline void pack( Stream& s, const signed_int& v ) {
Expand Down Expand Up @@ -285,6 +285,7 @@ namespace fc {
{ try {
bool b; fc::raw::unpack( s, b );
if( b ) { v = T(); fc::raw::unpack( s, *v ); }
else { v.reset(); } // in case v has already has a value
} FC_RETHROW_EXCEPTIONS( warn, "optional<${type}>", ("type",fc::get_typename<T>::name() ) ) }

// std::vector<char>
Expand Down Expand Up @@ -635,6 +636,7 @@ namespace fc {
inline void unpack( Stream& s, std::list<T>& value ) {
unsigned_int size; fc::raw::unpack( s, size );
FC_ASSERT( size.value <= MAX_NUM_ARRAY_ELEMENTS );
value.clear();
while( size.value-- ) {
T i;
fc::raw::unpack( s, i );
Expand All @@ -658,6 +660,7 @@ namespace fc {
inline void unpack( Stream& s, std::set<T>& value ) {
unsigned_int size; fc::raw::unpack( s, size );
FC_ASSERT( size.value <= MAX_NUM_ARRAY_ELEMENTS );
value.clear();
for( uint64_t i = 0; i < size.value; ++i )
{
T tmp;
Expand Down
191 changes: 191 additions & 0 deletions libraries/libfc/test/io/test_raw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,195 @@ BOOST_AUTO_TEST_CASE(struct_serialization) {

}

// Verify std::optional is unpacked correctly, especially an empty optional will always
// be unpacked to an empty optional even the target is not empty
BOOST_AUTO_TEST_CASE(unpacking_optional) {
// source is empty
char buff[8];
datastream<char*> ds(buff, sizeof(buff));
std::optional<uint32_t> s; // no value
fc::raw::pack(ds, s);

{ // target has value. This test used to fail.
std::optional<uint32_t> t = 10;
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((s == t));
}

{ // target reused for multiple unpackings. This test used to fail.
char buff[8];
datastream<char*> ds1(buff, sizeof(buff));
std::optional<uint32_t> s1 = 15;
fc::raw::pack(ds1, s1);

std::optional<uint32_t> t; // target is empty initially

// Unpacking to t the first time so t has value
ds1.seekp(0);
fc::raw::unpack(ds1, t);
BOOST_TEST((s1 == t));

// Unpacking to t the second time. Afterwards, t does not have value.
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((s == t));
}

{ // target is empty.
std::optional<uint32_t> t;
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((s == t));
}

// Source has value
s = 5;
ds.seekp(0);
fc::raw::pack(ds, s);

{ // target has value.
std::optional<uint32_t> t = 10;
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((s == t));
}

{ // target is empty.
std::optional<uint32_t> t;
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((s == t));
}
}

// Verify std::shared_ptr is unpacked correctly, especially a null shared_ptr will always
// be unpacked to a null shared_ptr even if the target was not null.
BOOST_AUTO_TEST_CASE(packing_shared_ptr) {
// source is null
char buff[8];
datastream<char*> ds(buff, sizeof(buff));
std::shared_ptr<uint32_t> s; // null_ptr
fc::raw::pack(ds, s);

{ // target has value. This test used to fail.
std::shared_ptr<uint32_t> t = std::make_shared<uint32_t>(10);
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST(!t);
}

{ // target is null.
std::shared_ptr<uint32_t> t;
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST(!t);
}

// source is not null
ds.seekp(0);
s = std::make_shared<uint32_t>(50);
fc::raw::pack(ds, s);

{ // target has value.
std::shared_ptr<uint32_t> t = std::make_shared<uint32_t>(10);
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((*s == *t));
}

{ // target is null.
std::shared_ptr<uint32_t> t;
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((*s == *t));
}
}

// Verify std::set is unpacked correctly, especially an empty set will always
// be unpacked to an empty set even if the target was not empty.
BOOST_AUTO_TEST_CASE(packing_set) {
//==== source empty
char buff[16];
datastream<char*> ds(buff, sizeof(buff));
std::set<uint32_t> s; // empty
fc::raw::pack(ds, s);

{ // target is not empty. This test used to fail.
std::set<uint32_t> t {10};
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST(t.empty());
}

{ // target is empty.
std::set<uint32_t> t;
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST(t.empty());
}

// Source has values
ds.seekp(0);
s = {1, 2};
fc::raw::pack(ds, s);

{ // target is not empty. This test used to fail (ending up with {1, 2, 3}).
std::set<uint32_t> t {3};
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((s == t));
}

{ // target is empty.
std::set<uint32_t> t;
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((s == t));
}
}

// Verify std::list is unpacked correctly, especially an empty list will always
// be unpacked to an empty list even if the target was not empty.
BOOST_AUTO_TEST_CASE(packing_list) {
//==== source empty
char buff[16];
datastream<char*> ds(buff, sizeof(buff));
std::list<uint32_t> s; // empty
fc::raw::pack(ds, s);

{ // target has value. This test used to fail.
std::list<uint32_t> t {10};
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((t.size() == 0));
}

{ // target is empty.
std::list<uint32_t> t;
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((t.size() == 0));
}

// Source has values
ds.seekp(0);
s = {1, 2};
fc::raw::pack(ds, s);

{ // target is not empty. This test used to fail (ending up with {1, 2, 3}).
std::list<uint32_t> t {3};
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((s == t));
}

{ // target is empty.
std::list<uint32_t> t;
ds.seekp(0);
fc::raw::unpack(ds, t);
BOOST_TEST((s == t));
}
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit b045377

Please sign in to comment.