diff --git a/Changes b/Changes index 4b4cb51c77..b2ce5a95b8 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,11 @@ 10.4.x.x (relative to 10.4.2.1) ======== +Improvements +------------ + +- Added string constructor and static `fromString` function to `IECore.MurmurHash`. + Build ----- diff --git a/include/IECore/MurmurHash.h b/include/IECore/MurmurHash.h index 55fdffff3d..91b20ea83a 100644 --- a/include/IECore/MurmurHash.h +++ b/include/IECore/MurmurHash.h @@ -61,6 +61,9 @@ class IECORE_API MurmurHash inline MurmurHash(); inline MurmurHash( const MurmurHash &other ); + // Construct directly from string representation + explicit MurmurHash( const std::string &repr ); + // Construct directly from known internal values inline MurmurHash( uint64_t h1, uint64_t h2 ); @@ -84,6 +87,7 @@ class IECORE_API MurmurHash inline bool operator < ( const MurmurHash &other ) const; std::string toString() const; + static MurmurHash fromString( const std::string &repr ); // Access internal storage for special cases inline uint64_t h1() const; diff --git a/src/IECore/MurmurHash.cpp b/src/IECore/MurmurHash.cpp index 21bda502cc..80dd523309 100644 --- a/src/IECore/MurmurHash.cpp +++ b/src/IECore/MurmurHash.cpp @@ -33,19 +33,63 @@ ////////////////////////////////////////////////////////////////////////// #include "IECore/MurmurHash.h" +#include "IECore/Exception.h" + +#include #include #include using namespace IECore; -std::string MurmurHash::toString() const +namespace +{ + +std::string internalToString( uint64_t const h1, uint64_t const h2 ) { std::stringstream s; - s << std::hex << std::setfill( '0' ) << std::setw( 16 ) << m_h1 << std::setw( 16 ) << m_h2; + s << std::hex << std::setfill( '0' ) << std::setw( 16 ) << h1 << std::setw( 16 ) << h2; return s.str(); } +void internalFromString( const std::string &repr, uint64_t &h1, uint64_t &h2 ) +{ + if( repr.length() != static_cast( 32 ) ) + { + throw Exception( + boost::str( + boost::format( + "Invalid IECore::MurmurHash string representation \"%s\", must have 32 characters" ) + % repr + ) ); + } + + std::stringstream s; + s.str( repr.substr( 0, 16 ) ); + s >> std::hex >> h1; + s.clear(); + s.str( repr.substr( 16, 16 ) ); + s >> std::hex >> h2; +} + +} // namespace + +MurmurHash::MurmurHash( const std::string &repr ) + : m_h1( 0 ), m_h2( 0 ) +{ + internalFromString( repr, m_h1, m_h2 ); +} + +std::string MurmurHash::toString() const +{ + return internalToString( m_h1, m_h2 ); +} + +MurmurHash MurmurHash::fromString( const std::string &repr ) +{ + return MurmurHash( repr ); +} + std::ostream &IECore::operator << ( std::ostream &o, const MurmurHash &hash ) { o << hash.toString(); diff --git a/src/IECorePython/MurmurHashBinding.cpp b/src/IECorePython/MurmurHashBinding.cpp index e12738ae39..649317be44 100644 --- a/src/IECorePython/MurmurHashBinding.cpp +++ b/src/IECorePython/MurmurHashBinding.cpp @@ -128,6 +128,7 @@ void IECorePython::bindMurmurHash() class_( "MurmurHash" ) .def( init<>() ) .def( init() ) + .def( init() ) .def( init() ) .def( "append", (MurmurHash &(MurmurHash::*)( const float & ))&MurmurHash::append, return_self<>() ) .def( "append", (MurmurHash &(MurmurHash::*)( const double & ))&MurmurHash::append, return_self<>() ) @@ -198,6 +199,8 @@ void IECorePython::bindMurmurHash() .def( "__str__", &MurmurHash::toString ) .def( "__hash__", &hash ) .def( "toString", &MurmurHash::toString ) + .def( "fromString", (MurmurHash (*)( const std::string & ))&MurmurHash::fromString ) + .staticmethod( "fromString" ) .def( "h1", &MurmurHash::h1 ) .def( "h2", &MurmurHash::h2 ) ; diff --git a/test/IECore/MurmurHashTest.py b/test/IECore/MurmurHashTest.py index f81c69da56..907a870885 100644 --- a/test/IECore/MurmurHashTest.py +++ b/test/IECore/MurmurHashTest.py @@ -34,6 +34,7 @@ import unittest import imath +import six import IECore @@ -45,6 +46,16 @@ def testConstructor( self ) : self.assertEqual( str( h ), "0" * 32 ) self.assertEqual( h, IECore.MurmurHash() ) + def testStringConstructor( self ) : + + h = IECore.MurmurHash() + h.append( 1 ) + h.append( "hello" ) + + s = h.toString() + hs = IECore.MurmurHash( s ) + self.assertEqual( h, hs ) + def testCopyConstructor( self ) : h = IECore.MurmurHash() @@ -54,6 +65,28 @@ def testCopyConstructor( self ) : self.assertEqual( h, IECore.MurmurHash( h ) ) self.assertNotEqual( h, IECore.MurmurHash() ) + def testRepr( self ) : + + h = IECore.MurmurHash() + h.append( 42 ) + h.append( "world" ) + h2 = eval( repr( h ) ) + + self.assertEqual( h, h2 ) + + def testFromString( self ) : + + h = IECore.MurmurHash() + h.append( 1 ) + h.append( "hello" ) + + s = h.toString() + hs = IECore.MurmurHash.fromString( s ) + self.assertEqual( h, hs ) + + with six.assertRaisesRegex( self, Exception, ".*must have 32 characters.*" ) : + IECore.MurmurHash.fromString( "InvalidStringRepresentation" ) + def testAppend( self ) : h = IECore.MurmurHash()