Boost logo

Boost-Commit :

From: dwalker07_at_[hidden]
Date: 2008-06-20 02:37:53


Author: dlwalker
Date: 2008-06-20 02:37:52 EDT (Fri, 20 Jun 2008)
New Revision: 46547
URL: http://svn.boost.org/trac/boost/changeset/46547

Log:
Completed functionality (including hashing) except for serialization; included corresponding tests; made the example program functional
Text files modified:
   sandbox/md5/boost/coding/md5.hpp | 795 ++++++++++++++++++++++++++++++++++++++-
   sandbox/md5/libs/coding/example/md5check.cpp | 9
   sandbox/md5/libs/coding/src/md5.cpp | 379 ++++++++++++++++++
   sandbox/md5/libs/coding/test/md5_computer_test.cpp | 507 ++++++++++++++++++++++++
   4 files changed, 1640 insertions(+), 50 deletions(-)

Modified: sandbox/md5/boost/coding/md5.hpp
==============================================================================
--- sandbox/md5/boost/coding/md5.hpp (original)
+++ sandbox/md5/boost/coding/md5.hpp 2008-06-20 02:37:52 EDT (Fri, 20 Jun 2008)
@@ -20,17 +20,23 @@
 #include <boost/coding_fwd.hpp>
 
 #include <boost/array.hpp> // for boost::array
+#include <boost/assert.hpp> // for BOOST_ASSERT
+#include <boost/cstdint.hpp> // for boost::uint_least8_t
+#include <boost/foreach.hpp> // for BOOST_FOREACH
 #include <boost/integer.hpp> // for boost::uint_t
 #include <boost/serialization/access.hpp> // for boost::serialization::access
+#include <boost/static_assert.hpp> // for BOOST_STATIC_ASSERT
 #include <boost/typeof/typeof.hpp> // for BOOST_AUTO
 
-#include <algorithm> // for std::equal
-#include <cstddef> // for std::size_t
+#include <algorithm> // for std::equal, swap, copy
+#include <climits> // for CHAR_BIT
+#include <cstddef> // for std::size_t, NULL
 #include <cstring> // for std::strlen
 #include <ios> // for std::ios
 #include <istream> // for std::basic_istream
 #include <locale> // for std::use_facet, ctype
 #include <ostream> // for std::basic_ostream
+#include <utility> // for std::make_pair
 
 
 namespace boost
@@ -116,8 +122,13 @@
         and other algorithms that can return a post-use function object.
 
         \see boost::coding::md5_computer
- \see boost::coding::md5_computer::byte_applicator
- \see boost::coding::md5_computer::bits
+ \see #byte_applicator
+ \see #bits
+
+ \todo The current store-by-reference semantics for the owning computer
+ means that said owner is always changed during an application of
+ \c *this. Maybe write-back to the owner should only be explicit
+ with the assignment operator.
      */
     class bit_applicator
     {
@@ -127,7 +138,7 @@
     public:
         //! Application
         void operator ()( bool v );
- //! Copy assignment
+ //! Copy-assignment
         bit_applicator & operator =( bit_applicator const &c );
 
     }; // bit_applicator
@@ -144,8 +155,13 @@
         post-use function object.
 
         \see boost::coding::md5_computer
- \see boost::coding::md5_computer::bit_applicator
- \see boost::coding::md5_computer::bytes
+ \see #bit_applicator
+ \see #bytes
+
+ \todo The current store-by-reference semantics for the owning computer
+ means that said owner is always changed during an application of
+ \c *this. Maybe write-back to the owner should only be explicit
+ with the assignment operator.
      */
     class byte_applicator
     {
@@ -154,12 +170,15 @@
         explicit byte_applicator( md5_computer &p ) : parent_( &p ) {};
     public:
         //! Application
- void operator ()( unsigned char v );
- //! Copy assignment
+ void operator ()( unsigned char v );
+ //! Copy-assignment
         byte_applicator & operator =( byte_applicator const &c );
 
     }; // byte_applicator
 
+ // Implementation constants
+ static std::size_t const words_per_block = 16u; // RFC 1321, section 3.4
+
 public:
     // Special application interface
     /** \brief Proxy for bit-oriented application interface
@@ -170,7 +189,11 @@
         other's owner's state to <code>*this</code>, enabling algorithms that
         return updated function objects to work.
 
- \see boost::coding::md5_computer::bit_applicator
+ \attention Since #bit_applicator stores a non-constant reference to its
+ owner, the owner will be change through any application
+ through \c bits, even if \c bits is passed by value.
+
+ \see #bit_applicator
      */
     bit_applicator bits;
     /** \brief Proxy for byte-oriented application interface
@@ -181,7 +204,11 @@
         copy the other's owner's state to <code>*this</code>, enabling
         algorithms that return updated function objects to work.
 
- \see boost::coding::md5_computer::byte_applicator
+ \attention Since #byte_applicator stores a non-constant reference to
+ its owner, the owner will be change through any application
+ through \c bytes, even if \c bytes is passed by value.
+
+ \see #byte_applicator
      */
     byte_applicator bytes;
 
@@ -192,8 +219,29 @@
         message length, which can also be processed as two words, all as given
         in RFC 1321, section 3.2, paragraph 1.
      */
- static int const significant_bits_per_length = 2 *
+ static int const significant_bits_per_length = 2 *
      md5_digest::bits_per_word;
+ /** \brief Number of bits in hash queue
+
+ Represents the number of submitted bits that are queued until that queue
+ is emptied as a single block to be hashed, implied from RFC 1321,
+ section 3.4. The count of unhashed bits is always less than this value.
+ (The input processing member functions trigger a hash right after a bit
+ fills the queue.)
+ */
+ static std::size_t const bits_per_block = words_per_block *
+ md5_digest::bits_per_word;
+
+ /** \brief Hashing sine table
+
+ Sample of the table described in RFC 1321, section 3.4, paragraph 4.
+ Its values are taken directly from the "MD5Transform" function in the
+ RFC's section A.3, and are not computed. Of course, the index is
+ zero-based (C++) instead of one-based (RFC).
+
+ \see #generate_hashing_table
+ */
+ static array<md5_digest::word_type, 64> const hashing_table;
 
     // Types
     /** \brief Type of checksums
@@ -250,7 +298,7 @@
 
     //! Copies out the unhashed bits
     template < typename OutputIterator >
- OutputIterator copy_unbuffered( OutputIterator o );//@}
+ OutputIterator copy_unbuffered( OutputIterator o ) const;//@}
 
     /*! \name Bit-stream reading */ //@{
     // Input processing
@@ -269,11 +317,23 @@
     //! Enters a range of bytes in memory for hashing
     void process_block( void const *bytes_begin, void const *bytes_end );
     //! Enters a byte buffer in memory for hashing
- void process_bytes( void const *buffer, size_type byte_count );//@}
+ void process_bytes( void const *buffer, size_type byte_count );
+
+ //! Enters an octet for hashing
+ void process_octet( uint_least8_t octet );
+ //! Enters a word for hashing
+ void process_word( md5_digest::word_type word );
+ //! Enters a double-word for hashing
+ void process_double_word( length_type dword );//@}
 
     /*! \name Message-digest writing */ //@{
     // Output processing
     //! Returns the message digest, assuming all bits have been hashed
+ /** Provides the computed check-sum of all the submitted bits. (The queued
+ bits are temporarily hashed with a special finishing procedure.)
+
+ \return The check-sum (i.e. message digest).
+ */
     value_type checksum() const;//@}
 
     /*! \name Operators */ //@{
@@ -283,15 +343,31 @@
 
     //! Equals
     bool operator ==( md5_computer const &c ) const;
- //! Not-equal
+ //! Not-equals
     bool operator !=( md5_computer const &c ) const;
 
     //! Application
     value_type operator ()() const;//@}
 
+ // Extras
+ //! Creates a copy of #hashing_table using calculated, not static, values
+ /** Constructs the hashing sine table based on the directions given in RFC
+ 1321, section 3.4, paragraph 4. It should give the same values as
+ #hashing_table, but it's dependent on the quality of the environment's
+ math library, thereby giving a test of the environment.
+
+ \return The computed hashing sine table
+
+ \relates #hashing_table
+ */
+ static array<md5_digest::word_type, 64> generate_hashing_table();
+
 private:
+ // Bad if computers someday get so big that a byte overflows the queue!
+ BOOST_STATIC_ASSERT( bits_per_block > CHAR_BIT );
+
     // State maintainence
- bool test_invariant() const;
+ bool test_invariant() const { return true; } // Nothing special right now
 
     // Serialization
     friend class serialization::access;
@@ -302,17 +378,17 @@
      void serialize( Archive &ar, const unsigned int version );//@}
        // may have to do save/load split; support XML archives
 
- // Implementation types & constants
+ // Implementation functions
+ void update_hash();
+
+ // Implementation types
+ typedef md5_computer self_type;
+
     typedef uint_fast64_t ilength_type;
       // replace w/ uint_t<significant_bits_per_length>::fast
     typedef uint_t<md5_digest::bits_per_word>::fast iword_type;
     typedef array<iword_type, md5_digest::words_per_digest> ibuffer_type;
 
- static std::size_t const words_per_block = 16u;
- // from RFC 1321, section 3.4
- static std::size_t const bits_per_block = words_per_block *
- md5_digest::bits_per_word;
-
     typedef array<bool, bits_per_block> block_type;
 
     // (Computation) member data
@@ -561,7 +637,7 @@
     this->parent_->process_bit( v );
 }
 
-/** Calls <code>this-&lt;<var>p</var> = <var>c.p</var></code>, where
+/** Calls <code>this-&gt;<var>p</var> = <var>c.p</var></code>, where
     \p p holds the owning \c md5_computer of a particular proxy.
 
     \return <code>*this</code>
@@ -575,7 +651,7 @@
     md5_computer::bit_applicator const & c
 )
 {
- this->parent_ = c.parent_;
+ *this->parent_ = *c.parent_;
     return *this;
 }
 
@@ -593,7 +669,7 @@
     this->parent_->process_byte( v );
 }
 
-/** Calls <code>this-&lt;<var>p</var> = <var>c.p</var></code>, where
+/** Calls <code>this-&gt;<var>p</var> = <var>c.p</var></code>, where
     \p p holds the owning \c md5_computer of a particular proxy.
 
     \return <code>*this</code>
@@ -607,10 +683,671 @@
     md5_computer::byte_applicator const & c
 )
 {
- this->parent_ = c.parent_;
+ *this->parent_ = *c.parent_;
+ return *this;
+}
+
+
+// MD5 message-digest computation constructor definitions ------------------//
+
+/** Constructs a \c md5_computer set to initial conditions. That is, with the
+ buffer initialized as in RFC 1321, section 3.3, and no bits counted as read
+ or currently left to be hashed.
+
+ \post <code>#bits_read() == 0</code>
+ \post <code>#last_buffer() == { 0x67452301, 0xEFCDAB89, 0x98BACDFE,
+ 0x10325476 }</code>
+ \post <code>#copy_unbuffered(<var>o</var>)</code> leaves \p o unused
+ \post \c #bits and \c #bytes point to \c *this
+ */
+inline
+md5_computer::md5_computer()
+ : bits( *this ), bytes( *this )
+ , length_(), buffer_( self_type::initial_buffer_ ), unbuffered_()
+{
+ BOOST_ASSERT( this->test_invariant() );
+}
+
+/** Constructs a \c md5_computer to the same computation state as <var>c</var>.
+
+ \param c The original object to be copied.
+
+ \post <code>#bits_read() == <var>c</var>.bits_read()</code>
+ \post <code>#last_buffer() == <var>c</var>.last_buffer()</code>
+ \post Given <code>#copy_unbuffered(<var>o1</var>)</code> and
+ <code><var>c</var>.copy_unbuffered(<var>o2</var>)</code>, where both
+ \p o1 and \p o2 are random access iterators to different container
+ segments that each have at least <code>#bits_unbuffered()</code>
+ elements available, <code>std::equal( o1, o1 + bits_unbuffered(), o2
+ ) == true</code>
+ \post \c #bits and \c #bytes point to \c *this
+ */
+inline
+md5_computer::md5_computer( md5_computer const &c )
+ : bits( *this ), bytes( *this )
+ , length_( c.length_ ), buffer_( c.buffer_ ), unbuffered_( c.unbuffered_ )
+{
+ BOOST_ASSERT( this->test_invariant() );
+}
+
+
+// MD5 message-digest computation inspector member function definitions ----//
+
+/** Returns the number of bits that have been processed, both those that have
+ been hashed and those that are on queue. Only the low-order 64 bits of
+ count are significant and kept/reliable.
+
+ \return How many bits have been submitted, hashed and queued.
+ */
+inline
+md5_computer::length_type
+md5_computer::bits_read() const
+{
+ return this->length_;
+}
+
+/** Returns the number of bits that have not been hashed. Hashing occurs only
+ after every \c #bits_per_block bit entries, so this member function can
+ confirm queued stragglers. (The identities of hashed bits are not
+ retrievable.)
+
+ \return How many bits are queued to be hashed.
+
+ \see #bits_read()
+ \see #bits_per_block
+ */
+inline
+md5_computer::length_type
+md5_computer::bits_unbuffered() const
+{
+ return this->length_ % self_type::bits_per_block;
+}
+
+/** Returns the checksum of all the bits that have been hashed so far. Hashing
+ occurs only after every \c #bits_per_block bit entries, so check
+ \c #bits_unbuffered() for any queued stragglers.
+
+ \return The current state of the MD buffer, not counting any unhashed bits.
+ */
+inline
+md5_computer::buffer_type
+md5_computer::last_buffer() const
+{
+ // boost::array has no constructors (since it's POD), that means that if
+ // buffer_type and ibuffer_type differ, we need to convert via assignment.
+ buffer_type r;
+
+ r = this->buffer_;
+ return r;
+}
+
+/** Copies the last submitted bits that have not yet hashed into the running
+ checksum, starting from the oldest submission. Use \c #bits_unbuffered()
+ for advance notice of how many iterations are done. (Always less than
+ \c #bits_per_block elements are copied.)
+
+ \pre At least \c #bits_unbuffered() more elements are free to be created
+ and/or assigned through \p o
+
+ \tparam OutputIterator The type of the iterator submitted. It should match
+ the requirements of either an output or a forward
+ (or above) mutable iterator over something that can
+ receive \c bool values via dereferenced assignment.
+
+ \param o The iterator starting the destination range.
+
+ \return \p o after copying.
+
+ \see #bits_unbuffered()
+ \see #bits_per_block
+ */
+template < typename OutputIterator >
+inline
+OutputIterator
+md5_computer::copy_unbuffered( OutputIterator o ) const
+{
+ return std::copy( this->unbuffered_.begin(), this->unbuffered_.begin() +
+ this->bits_unbuffered(), o );
+}
+
+
+// MD5 message-digest computation assignment member function definitions ---//
+
+/** Changes an object to be like it was default-constructed.
+
+ \post <code>#bits_read() == 0</code>
+ \post <code>#last_buffer() == { 0x67452301, 0xEFCDAB89, 0x98BACDFE,
+ 0x10325476 }</code>
+ \post <code>#copy_unbuffered(<var>o</var>)</code> leaves \p o unused
+
+ \see #md5_computer()
+ */
+inline
+void
+md5_computer::reset()
+{
+ // The "bits" and "bytes" members don't/can't reset. The "unbuffered_"
+ // member doesn't need it either since "length_" reset will cause existing
+ // elements of "unbuffered_" to be ignored.
+ this->length_ = 0u;
+ this->buffer_ = self_type::initial_buffer_;
+
+ BOOST_ASSERT( this->test_invariant() );
+}
+
+/** Changes an object to be like the given object. Only the computation
+ elements are copied; no function object proxies are reseated.
+
+ \param c The object with the new state to be copied.
+
+ \post <code>#bits_read() == <var>c</var>.bits_read()</code>
+ \post <code>#last_buffer() == <var>c</var>.last_buffer()</code>
+ \post Given <code>#copy_unbuffered(<var>o1</var>)</code> and
+ <code><var>c</var>.copy_unbuffered(<var>o2</var>)</code>, where both
+ \p o1 and \p o2 are random access iterators to different container
+ segments that each have at least <code>#bits_unbuffered()</code>
+ elements available, <code>std::equal( o1, o1 + bits_unbuffered(), o2
+ ) == true</code>
+ \post \c #bits and \c #bytes \e still point to \c *this
+
+ \see #md5_computer(md5_computer const&)
+ */
+inline
+void
+md5_computer::assign( md5_computer const &c )
+{
+ // Don't/can't reseat the function object proxies; only the elements of
+ // "unbuffered_" that will remain significant need to be copied.
+ this->length_ = c.length_;
+ this->buffer_ = c.buffer_;
+ std::copy( c.unbuffered_.begin(), c.unbuffered_.begin() +
+ c.bits_unbuffered(), this->unbuffered_.begin() );
+
+ BOOST_ASSERT( this->test_invariant() );
+}
+
+/** Swaps the content of this object with another. Only the computation
+ elements are changed; no function object proxies are reseated.
+
+ \param other The other object to trade state with this object.
+
+ \post <code>*this == <var>old_other</var> &amp;&amp; <var>old_this</var> ==
+ <var>other</var></code>
+ */
+inline
+void
+md5_computer::swap( md5_computer &other )
+{
+ // Use the appropriate swap via Koeing look-up
+ using std::swap;
+
+ // Swap the computation members (don't reseat the function object proxies)
+ swap( this->length_, other.length_ );
+ swap( this->buffer_, other.buffer_ );
+ swap( this->unbuffered_, other.unbuffered_ );
+
+ BOOST_ASSERT( this->test_invariant() );
+ BOOST_ASSERT( other.test_invariant() );
+}
+
+
+// MD5 message-digest computation bit-input member function definitions ----//
+
+/** Submits a single bit for computation. The bit is queued; if this bit fills
+ the queue, the queue's bits are hashed and the queue is emptied.
+
+ \param bit The bit value to be submitted.
+
+ \post <code>#bits_read() == <var>old_this</var>.bits_read() + 1</code>
+ \post <code>#bits_unbuffered() == bits_read() % #bits_per_block ? 0 :
+ <var>old_this</var>.bits_unbuffered() + 1</code>
+ \post <code>#last_buffer() == bits_read() % bits_per_block ?
+ <var>new_value</var> : <var>old_this</var>.last_buffer()</code> (The
+ new value is computed with the algorithm described in RFC 1321,
+ section 3.4.)
+ \post If <code>bits_read() % bits_per_block == 0</code>, then
+ <code>#copy_unbuffered(<var>o1</var>)</code> leaves \p o1 unused,
+ otherwise <code>copy_unbuffered(<var>o2</var>) -
+ <var>old_this</var>.copy_unbuffered(<var>o2</var>) == 1</code>
+ (assuming that \p o2 is, at least, a forward iterator)
+ */
+inline
+void
+md5_computer::process_bit( bool bit )
+{
+ this->unbuffered_[ this->bits_unbuffered() ] = bit;
+ if ( 0u == (++this->length_ % self_type::bits_per_block) )
+ this->update_hash();
+ BOOST_ASSERT( this->test_invariant() );
+}
+
+/** Submits part of a byte for computation. Bits are submitted starting from
+ the highest-order bit to the lowest.
+
+ \pre <code>0 &lt; <var>bit_count</var> &lt;= CHAR_BIT</code>
+
+ \param bits The byte from which the values are submitted.
+ \param bit_count The number of bits to submit, starting from the
+ 2<sup><var>bit_count</var> - 1</sup> place down to the
+ ones-place.
+
+ \post <code>#bits_read() == <var>old_this</var>.bits_read() +
+ <var>bit_count</var></code>
+ \post <code>#bits_unbuffered() == (<var>old_this</var>.bits_unbuffered() +
+ <var>bit_count</var>) % bits_per_block</code>
+ \post <code>#last_buffer() == <var>old_this</var>.bits_unbuffered() +
+ <var>bit_count</var> &gt;= bits_per_block ? <var>new_value</var> :
+ <var>old_this</var>.last_buffer()</code> (The new value is computed
+ with the algorithm described in RFC 1321, section 3.4.)
+ \post If <code>bits_read() % bits_per_block == 0</code>, then
+ <code>#copy_unbuffered(<var>o1</var>)</code> leaves \p o1 unused,
+ otherwise if <code><var>old_this</var>.bits_unbuffered() +
+ <var>bit_count</var> &lt; bits_per_block,</code> then
+ <code>copy_unbuffered(<var>o2</var>) -
+ <var>old_this</var>.copy_unbuffered(<var>o2</var>) ==
+ <var>bit_count</var></code> (assuming that \p o2 is, at least, a
+ forward iterator), otherwise the range \p o3 to
+ <code>copy_unbuffered(<var>o3</var>)</code> contains the
+ <code><var>old_this</var>.bits_unbuffered() + <var>bit_count</var> -
+ bits_per_block</code> lowest-order bits of \p bits
+
+ \see #process_bit(bool)
+ */
+inline
+void
+md5_computer::process_bits
+(
+ unsigned char bits,
+ md5_computer::size_type bit_count
+)
+{
+ BOOST_ASSERT( (0u < bit_count) && (bit_count <= CHAR_BIT) );
+ for ( unsigned char m = 0x01u << (bit_count - 1u) ; bit_count-- ; m >>= 1 )
+ this->process_bit( bits & m );
+}
+
+/** Submits multiple copies of a single bit value for computation.
+
+ \param value The bit value to be submitted.
+ \param bit_count The number of bits to submit.
+
+ \post <code>#bits_read() == <var>old_this</var>.bits_read() +
+ <var>bit_count</var></code>
+ \post <code>#bits_unbuffered() == (<var>old_this</var>.bits_unbuffered() +
+ <var>bit_count</var>) % bits_per_block</code>
+ \post <code>#last_buffer() == (<var>old_this</var>.bits_unbuffered() +
+ <var>bit_count</var> &gt;= bits_per_block) ? <var>new_value</var> :
+ <var>old_this</var>.last_buffer()</code> (The new value is computed
+ with the algorithm described in RFC 1321, section 3.4.)
+ \post If <code>bits_read() % bits_per_block == 0</code>, then
+ <code>#copy_unbuffered(<var>o1</var>)</code> leaves \p o1 unused,
+ otherwise if <code><var>old_this</var>.bits_unbuffered() +
+ <var>bit_count</var> &lt; bits_per_block,</code> then
+ <code>copy_unbuffered(<var>o2</var>) -
+ <var>old_this</var>.copy_unbuffered(<var>o2</var>) ==
+ <var>bit_count</var></code> (assuming that \p o2 is, at least, a
+ forward iterator), otherwise the range \p o3 to
+ <code>copy_unbuffered(<var>o3</var>)</code> contains
+ <code>(<var>old_this</var>.bits_unbuffered() + <var>bit_count</var>)
+ % bits_per_block</code> copies of \p value
+
+ \see #process_bit(bool)
+ */
+inline
+void
+md5_computer::process_bit_copies
+(
+ bool value,
+ md5_computer::size_type bit_count
+)
+{
+ while ( bit_count-- )
+ this->process_bit( value );
+}
+
+/** Submits a byte for computation. The bits are submitted starting from the
+ highest-order bit to the lowest.
+
+ \param byte The byte value to be submitted.
+
+ \post <code>#bits_read() == <var>old_this</var>.bits_read() +
+ CHAR_BIT</code>
+ \post <code>#bits_unbuffered() == (<var>old_this</var>.bits_unbuffered() +
+ CHAR_BIT) % bits_per_block</code>
+ \post <code>#last_buffer() == (<var>old_this</var>.bits_unbuffered() +
+ CHAR_BIT &gt;= bits_per_block) ? <var>new_value</var> :
+ <var>old_this</var>.last_buffer()</code> (The new value is computed
+ with the algorithm described in RFC 1321, section 3.4.)
+ \post If <code>bits_read() % bits_per_block == 0</code>, then
+ <code>#copy_unbuffered(<var>o1</var>)</code> leaves \p o1 unused,
+ otherwise if <code><var>old_this</var>.bits_unbuffered() + CHAR_BIT
+ &lt; bits_per_block,</code> then <code>copy_unbuffered(<var>o2</var>)
+ - <var>old_this</var>.copy_unbuffered(<var>o2</var>) ==
+ CHAR_BIT</code> (assuming that \p o2 is, at least, a forward
+ iterator), otherwise the range \p o3 to
+ <code>copy_unbuffered(<var>o3</var>)</code> contains the
+ <code><var>old_this</var>.bits_unbuffered() + CHAR_BIT -
+ bits_per_block</code> lowest-order bits of \p byte
+
+ \see #process_bits(unsigned char,#size_type)
+ */
+inline
+void
+md5_computer::process_byte( unsigned char byte )
+{
+ this->process_bits( byte, CHAR_BIT );
+}
+
+/** Submits multiple copies of a single byte value for computation.
+
+ \param value The byte value to be submitted.
+ \param byte_count The number of bytes to submit.
+
+ \post <code>#bits_read() == <var>old_this</var>.bits_read() +
+ <var>byte_count</var> * CHAR_BIT</code>
+ \post <code>#bits_unbuffered() == (<var>old_this</var>.bits_unbuffered() +
+ <var>byte_count</var> * CHAR_BIT) % bits_per_block</code>
+ \post <code>#last_buffer() == (<var>old_this</var>.bits_unbuffered() +
+ <var>byte_count</var> * CHAR_BIT &gt;= bits_per_block) ?
+ <var>new_value</var> : <var>old_this</var>.last_buffer()</code> (The
+ new value is computed with the algorithm described in RFC 1321,
+ section 3.4.)
+ \post If <code>bits_read() % bits_per_block == 0</code>, then
+ <code>#copy_unbuffered(<var>o1</var>)</code> leaves \p o1 unused,
+ otherwise if <code><var>old_this</var>.bits_unbuffered() +
+ <var>byte_count</var> * CHAR_BIT &lt; bits_per_block,</code> then
+ <code>copy_unbuffered(<var>o2</var>) -
+ <var>old_this</var>.copy_unbuffered(<var>o2</var>) ==
+ <var>bit_count</var> * CHAR_BIT</code> (assuming that \p o2 is, at
+ least, a forward iterator), otherwise the range \p o3 to
+ <code>copy_unbuffered(<var>o3</var>)</code> contains
+ <code>(<var>old_this</var>.bits_unbuffered() + <var>byte_count</var>
+ * CHAR_BIT) % bits_per_block</code> bits from copies of \p value
+
+ \see #process_byte(unsigned char)
+ */
+inline
+void
+md5_computer::process_byte_copies
+(
+ unsigned char value,
+ md5_computer::size_type byte_count
+)
+{
+ while ( byte_count-- )
+ this->process_byte( value );
+}
+
+/** Submits bytes, delimited by a pointer range, for computation.
+
+ \pre If \p bytes_begin is not \c NULL, then \p bytes_end has to be
+ reachable from \p bytes_begin via forward iterations of their
+ equivalent <code>unsigned char const *</code> values, otherwise
+ \p bytes_end has to be \c NULL too
+
+ \param bytes_begin The start of the byte range to be submitted.
+ \param bytes_end One-past-the-end of the byte range in \p bytes_begin.
+
+ \post <code>#bits_read() == <var>old_this</var>.bits_read() +
+ <var>L</var> * CHAR_BIT</code>, where \p L is number of bytes in the
+ range from \p bytes_begin to just before \p bytes_end
+ \post <code>#bits_unbuffered() == (<var>old_this</var>.bits_unbuffered() +
+ <var>L</var> * CHAR_BIT) % bits_per_block</code>
+ \post <code>#last_buffer() == (<var>old_this</var>.bits_unbuffered() +
+ <var>L</var> * CHAR_BIT &gt;= bits_per_block) ? <var>new_value</var>
+ : <var>old_this</var>.last_buffer()</code> (The new value is
+ computed with the algorithm described in RFC 1321, section 3.4.)
+ \post If <code>bits_read() % bits_per_block == 0</code>, then
+ <code>#copy_unbuffered(<var>o1</var>)</code> leaves \p o1 unused,
+ otherwise if <code><var>old_this</var>.bits_unbuffered() +
+ <var>L</var> * CHAR_BIT &lt; bits_per_block,</code> then
+ <code>copy_unbuffered(<var>o2</var>) -
+ <var>old_this</var>.copy_unbuffered(<var>o2</var>) ==
+ <var>L</var> * CHAR_BIT</code> (assuming that \p o2 is, at least, a
+ forward iterator), otherwise the range \p o3 to
+ <code>copy_unbuffered(<var>o3</var>)</code> contains
+ <code>(<var>old_this</var>.bits_unbuffered() + <var>L</var> *
+ CHAR_BIT) % bits_per_block</code> trailing bits from the byte range
+
+ \see #process_byte(unsigned char)
+ */
+inline
+void
+md5_computer::process_block( void const *bytes_begin, void const *bytes_end )
+{
+ BOOST_ASSERT( (bytes_begin == NULL) == (bytes_end == NULL) );
+ BOOST_FOREACH( unsigned char b, std::make_pair(static_cast<unsigned char
+ const *>( bytes_begin ), static_cast<unsigned char const *>( bytes_end )) )
+ this->process_byte( b );
+}
+
+/** Submits bytes, bounded by a pointer and length, for computation.
+
+ \pre If \p buffer is \c NULL, then \p byte_count must be zero
+
+ \param buffer The start of the byte range to be submitted.
+ \param byte_count Number of (leading) bytes to use from the range.
+
+ \post <code>#bits_read() == <var>old_this</var>.bits_read() +
+ <var>byte_count</var> * CHAR_BIT</code>
+ \post <code>#bits_unbuffered() == (<var>old_this</var>.bits_unbuffered() +
+ <var>byte_count</var> * CHAR_BIT) % bits_per_block</code>
+ \post <code>#last_buffer() == (<var>old_this</var>.bits_unbuffered() +
+ <var>byte_count</var> * CHAR_BIT &gt;= bits_per_block) ?
+ <var>new_value</var> : <var>old_this</var>.last_buffer()</code> (The
+ new value is computed with the algorithm described in RFC 1321,
+ section 3.4.)
+ \post If <code>bits_read() % bits_per_block == 0</code>, then
+ <code>#copy_unbuffered(<var>o1</var>)</code> leaves \p o1 unused,
+ otherwise if <code><var>old_this</var>.bits_unbuffered() +
+ <var>byte_count</var> * CHAR_BIT &lt; bits_per_block,</code> then
+ <code>copy_unbuffered(<var>o2</var>) -
+ <var>old_this</var>.copy_unbuffered(<var>o2</var>) ==
+ <var>byte_count</var> * CHAR_BIT</code> (assuming that \p o2 is, at
+ least, a forward iterator), otherwise the range \p o3 to
+ <code>copy_unbuffered(<var>o3</var>)</code> contains
+ <code>(<var>old_this</var>.bits_unbuffered() + <var>byte_count</var>
+ * CHAR_BIT) % bits_per_block</code> trailing bits from the buffer
+
+ \see #process_block(void const*,void const*)
+ */
+inline
+void
+md5_computer::process_bytes
+(
+ void const * buffer,
+ md5_computer::size_type byte_count
+)
+{
+ BOOST_ASSERT( buffer || !byte_count );
+ this->process_block( buffer, static_cast<unsigned char const *>(buffer) +
+ byte_count );
+}
+
+/** Submits an octet for computation. The bits are submitted starting from the
+ highest-order bit to the lowest.
+
+ \param octet The octet value to be submitted.
+
+ \post <code>#bits_read() == <var>old_this</var>.bits_read() + 8</code>
+ \post <code>#bits_unbuffered() == (<var>old_this</var>.bits_unbuffered() +
+ 8) % bits_per_block</code>
+ \post <code>#last_buffer() == (<var>old_this</var>.bits_unbuffered() + 8
+ &gt;= bits_per_block) ? <var>new_value</var> :
+ <var>old_this</var>.last_buffer()</code> (The new value is computed
+ with the algorithm described in RFC 1321, section 3.4.)
+ \post If <code>bits_read() % bits_per_block == 0</code>, then
+ <code>#copy_unbuffered(<var>o1</var>)</code> leaves \p o1 unused,
+ otherwise if <code><var>old_this</var>.bits_unbuffered() + 8 &lt;
+ bits_per_block,</code> then <code>copy_unbuffered(<var>o2</var>) -
+ <var>old_this</var>.copy_unbuffered(<var>o2</var>) == 8</code>
+ (assuming that \p o2 is, at least, a forward iterator), otherwise the
+ range \p o3 to <code>copy_unbuffered(<var>o3</var>)</code> contains
+ the <code><var>old_this</var>.bits_unbuffered() + 8 -
+ bits_per_block</code> lowest-order bits of \p octet
+
+ \see #process_bits(unsigned char,#size_type)
+ */
+inline
+void
+md5_computer::process_octet( uint_least8_t octet )
+{
+ this->process_bits( octet, 8u );
+}
+
+/** Submits 32-bit MD-word for computation. The word is submitted an octet at a
+ time, from the lowest-order octet to the highest.
+
+ \param word The word value to be submitted.
+
+ \post <code>#bits_read() == <var>old_this</var>.bits_read() + 32</code>
+ \post <code>#bits_unbuffered() == (<var>old_this</var>.bits_unbuffered() +
+ 32) % bits_per_block</code>
+ \post <code>#last_buffer() == (<var>old_this</var>.bits_unbuffered() + 32
+ &gt;= bits_per_block) ? <var>new_value</var> :
+ <var>old_this</var>.last_buffer()</code> (The new value is computed
+ with the algorithm described in RFC 1321, section 3.4.)
+ \post If <code>bits_read() % bits_per_block == 0</code>, then
+ <code>#copy_unbuffered(<var>o1</var>)</code> leaves \p o1 unused,
+ otherwise if <code><var>old_this</var>.bits_unbuffered() + 32 &lt;
+ bits_per_block,</code> then <code>copy_unbuffered(<var>o2</var>) -
+ <var>old_this</var>.copy_unbuffered(<var>o2</var>) == 32</code>
+ (assuming that \p o2 is, at least, a forward iterator), otherwise the
+ range \p o3 to <code>copy_unbuffered(<var>o3</var>)</code> contains
+ the <code><var>old_this</var>.bits_unbuffered() + 32 -
+ bits_per_block</code> lowest-order bits of \p word
+
+ \see #process_octet(boost::uint_least8_t)
+ */
+inline
+void
+md5_computer::process_word( md5_digest::word_type word )
+{
+ this->process_octet( word & 0xFFul );
+ this->process_octet( (word >> 8) & 0xFFul );
+ this->process_octet( (word >> 16) & 0xFFul );
+ this->process_octet( (word >> 24) & 0xFFul );
+}
+
+/** Submits 64-bit MD-length for computation. The double-word is submitted a
+ word at a time, first the low-order word then the high-order word.
+
+ \param dword The double-word value to be submitted.
+
+ \post <code>#bits_read() == <var>old_this</var>.bits_read() + 64</code>
+ \post <code>#bits_unbuffered() == (<var>old_this</var>.bits_unbuffered() +
+ 64) % bits_per_block</code>
+ \post <code>#last_buffer() == (<var>old_this</var>.bits_unbuffered() + 64
+ &gt;= bits_per_block) ? <var>new_value</var> :
+ <var>old_this</var>.last_buffer()</code> (The new value is computed
+ with the algorithm described in RFC 1321, section 3.4.)
+ \post If <code>bits_read() % bits_per_block == 0</code>, then
+ <code>#copy_unbuffered(<var>o1</var>)</code> leaves \p o1 unused,
+ otherwise if <code><var>old_this</var>.bits_unbuffered() + 64 &lt;
+ bits_per_block,</code> then <code>copy_unbuffered(<var>o2</var>) -
+ <var>old_this</var>.copy_unbuffered(<var>o2</var>) == 64</code>
+ (assuming that \p o2 is, at least, a forward iterator), otherwise the
+ range \p o3 to <code>copy_unbuffered(<var>o3</var>)</code> contains
+ the <code><var>old_this</var>.bits_unbuffered() + 64 -
+ bits_per_block</code> lowest-order bits of \p word
+
+ \see #process_word(boost::coding::md5_digest::word_type)
+ */
+inline
+void
+md5_computer::process_double_word( md5_computer::length_type dword )
+{
+ this->process_word( dword & 0xFFFFFFFFull );
+ this->process_word( (dword >> 32) & 0xFFFFFFFFull );
+}
+
+
+// MD5 message-digest structure member operator function definitions -------//
+
+/** Changes a MD5 message computer to have the same observable state as a given
+ computer. (No function object proxies are reseated, however.)
+
+ \param c The source object with the new state.
+
+ \return \c *this
+
+ \post <code>#bits_read() == <var>c</var>.bits_read()</code>
+ \post <code>#last_buffer() == <var>c</var>.last_buffer()</code>
+ \post Given <code>#copy_unbuffered(<var>o1</var>)</code> and
+ <code><var>c</var>.copy_unbuffered(<var>o2</var>)</code>, where both
+ \p o1 and \p o2 are random access iterators to different container
+ segments that each have at least <code>#bits_unbuffered()</code>
+ elements available, <code>std::equal( o1, o1 + bits_unbuffered(), o2
+ ) == true</code>
+ \post \c #bits and \c #bytes \e still point to \c *this
+
+ \see #assign(md5_computer const&)
+ */
+inline
+md5_computer &
+md5_computer::operator =( md5_computer const &c )
+{
+ this->assign( c );
     return *this;
 }
 
+/** Compares MD5 message computers for equivalence. Such computers are equal if
+ all of the corresponding parts of their significant state (data length,
+ running hash, and bit queue) are equal.
+
+ \param c The right-side operand to be compared.
+
+ \retval true \c *this and \p c are equivalent.
+ \retval false \c *this and \p c are not equivalent.
+
+ \see #bits_read()
+ \see #last_buffer()
+ \see #copy_unbuffered(OutputIterator)
+ */
+inline
+bool
+md5_computer::operator ==( md5_computer const &c ) const
+{
+ // Don't compare the function object proxies since they don't carry
+ // significant state. (Furthermore, they can't change once initalized and
+ // don't have any comparison operators.)
+ return ( this->length_ == c.length_ ) && ( this->buffer_ == c.buffer_ ) &&
+ std::equal( this->unbuffered_.begin(), this->unbuffered_.begin() +
+ this->bits_unbuffered(), c.unbuffered_.begin() );
+}
+
+/** Compares MD5 message computers for non-equivalence. Such computers are
+ unequal if at least one set of corresponding parts of their significant
+ state (data length, running hash, and bit queue) are unequal.
+
+ \param c The right-side operand to be compared.
+
+ \retval true \c *this and \p c are not equivalent.
+ \retval false \c *this and \p c are equivalent.
+
+ \see #operator==(md5_computer const&)const
+ */
+inline
+bool
+md5_computer::operator !=( md5_computer const &c ) const
+{
+ return !this->operator ==( c );
+}
+
+/** Computes the check-sum of the submitted data, through a standard generator
+ interface.
+
+ \return The generated check-sum.
+
+ \see #checksum()const
+ */
+inline
+md5_computer::value_type
+md5_computer::operator ()() const
+{
+ return this->checksum();
+}
+
 
 // MD5 message-digest computation miscellaneous function definitions -------//
 
@@ -634,8 +1371,8 @@
 void
 swap
 (
- md5_computer a,
- md5_computer b
+ md5_computer & a,
+ md5_computer & b
 )
 {
     a.swap( b );
@@ -657,6 +1394,8 @@
     \param byte_count The length of the data block to be processed, in bytes.
 
     \return The MD5 message digest of the data block.
+
+ \see boost::coding::md5_computer
  */
 inline
 md5_digest

Modified: sandbox/md5/libs/coding/example/md5check.cpp
==============================================================================
--- sandbox/md5/libs/coding/example/md5check.cpp (original)
+++ sandbox/md5/libs/coding/example/md5check.cpp 2008-06-20 02:37:52 EDT (Fri, 20 Jun 2008)
@@ -41,7 +41,7 @@
 )
 try
 {
- using boost::coding::md5_result;
+ using boost::coding::md5_digest;
     using std::cout;
 
     typedef std::istream_iterator<unsigned char> byte_read_iterator;
@@ -53,7 +53,7 @@
     checksummer.bytes = std::for_each( byte_read_iterator(std::cin),
      byte_read_iterator(), checksummer.bytes );
 
- md5_result const fingerprint = checksummer();
+ md5_digest const fingerprint = checksummer();
 
     // Use the results based on the program's mode
     switch ( argc )
@@ -68,10 +68,7 @@
         // argument
         try
         {
- md5_result const expected = boost::lexical_cast<md5_result>(
- argv[1] );
-
- if ( fingerprint == expected )
+ if ( fingerprint == boost::lexical_cast<md5_digest>(argv[ 1 ]) )
             {
                 cout << "The checksums matched (as '" << fingerprint << "')." <<
                  endl;

Modified: sandbox/md5/libs/coding/src/md5.cpp
==============================================================================
--- sandbox/md5/libs/coding/src/md5.cpp (original)
+++ sandbox/md5/libs/coding/src/md5.cpp 2008-06-20 02:37:52 EDT (Fri, 20 Jun 2008)
@@ -16,7 +16,167 @@
 
 #include <boost/coding/md5.hpp>
 
-#include <cstddef> // for std::size_t
+#include <boost/array.hpp> // for boost::array
+#include <boost/assert.hpp> // for BOOST_ASSERT
+#include <boost/integer/integer_mask.hpp> // for boost::low_bits_mask_t
+#include <boost/math/common_factor_rt.hpp> // for boost::math::gcd
+#include <boost/static_assert.hpp> // for BOOST_STATIC_ASSERT
+
+#include <algorithm> // for std::copy
+#include <cmath> // for std::sin, abs, ldexp, modf
+#include <cstddef> // for std::size_t
+#include <limits> // for std::numeric_limits
+#include <numeric> // for std::inner_product, partial_sum
+#include <valarray> // for std::valarray, etc.
+
+
+// Custom types/functions/templates ----------------------------------------//
+
+// Put custom types/templates and helper functions here
+namespace
+{
+
+// Auxillary functions from RFC 1321, section 3.4, paragraphs 1-3
+template < typename BitwiseType >
+class md5_f
+{
+public:
+ typedef BitwiseType result_type, first_argument_type, second_argument_type,
+ third_argument_type;
+
+ result_type operator ()( first_argument_type x, second_argument_type y,
+ third_argument_type z ) const
+ {
+ // First MD5 auxillary function F: selection (if x, then y, else z)
+ return ( x & y ) | ( ~x & z );
+ }
+};
+
+template < typename BitwiseType >
+class md5_g
+{
+public:
+ typedef BitwiseType result_type, first_argument_type, second_argument_type,
+ third_argument_type;
+
+ result_type operator ()( first_argument_type x, second_argument_type y,
+ third_argument_type z ) const
+ {
+ // Second MD5 auxillary function G: selection (if z, then x, else y)
+ return ( x & z ) | ( y & ~z );
+ }
+};
+
+template < typename BitwiseType >
+class md5_h
+{
+public:
+ typedef BitwiseType result_type, first_argument_type, second_argument_type,
+ third_argument_type;
+
+ result_type operator ()( first_argument_type x, second_argument_type y,
+ third_argument_type z ) const
+ {
+ // Third MD5 auxillary function H: parity (# of TRUE values is odd)
+ return x ^ y ^ z;
+ }
+};
+
+template < typename BitwiseType >
+class md5_i
+{
+public:
+ typedef BitwiseType result_type, first_argument_type, second_argument_type,
+ third_argument_type;
+
+ result_type operator ()( first_argument_type x, second_argument_type y,
+ third_argument_type z ) const
+ {
+ // Fourth MD5 auxillary function I: ? (???)
+ return y ^ ( x | ~z );
+ }
+};
+
+// Transfoming rotation constants
+int const md5_s[4][4] = { {7, 12, 17, 22}, {5, 9, 14, 20}, {4, 11, 16, 23}, {6,
+ 10, 15, 21} };
+
+// Rotating left-shift for bits
+template < typename Unsigned, int TotalRotated >
+class left_rotator
+{
+ typedef std::numeric_limits<Unsigned> limits_type;
+
+ BOOST_STATIC_ASSERT( limits_type::is_specialized && limits_type::is_integer
+ && (limits_type::radix == 2) && limits_type::is_bounded &&
+ !limits_type::is_signed && (limits_type::digits >= TotalRotated) );
+
+ static Unsigned const mask_;
+
+public:
+ typedef Unsigned result_type, first_argument_type;
+ typedef int second_argument_type;
+
+ result_type operator ()( first_argument_type x, second_argument_type n )
+ const
+ {
+ return ( (x << n) | (( x & mask_ ) >> ( TotalRotated - n )) ) & mask_;
+ }
+};
+
+template < typename Unsigned, int TotalRotated >
+Unsigned const left_rotator<Unsigned, TotalRotated>::mask_ =
+ boost::low_bits_mask_t<TotalRotated>::sig_bits;
+
+// Order of listed bits within a 32-bit word: octets are listed lowest-order
+// first, but the bits within octets are listed highest-order first! The order
+// is given in the appropriate power-of-two for that bit.
+boost::array<unsigned long, 32> const order_in_word = { {1ul << 7, 1ul << 6,
+ 1ul << 5, 1ul << 4, 1ul << 3, 1ul << 2, 1ul << 1, 1ul << 0, 1ul << 15, 1ul <<
+ 14, 1ul << 13, 1ul << 12, 1ul << 11, 1ul << 10, 1ul << 9, 1ul << 8, 1ul << 23,
+ 1ul << 22, 1ul << 21, 1ul << 20, 1ul << 19, 1ul << 18, 1ul << 17, 1ul << 16,
+ 1ul << 31, 1ul << 30, 1ul << 29, 1ul << 28, 1ul << 27, 1ul << 26, 1ul << 25,
+ 1ul << 24} };
+
+// The special operation repeatedly used for hashing
+template < template <typename> class TernaryFuncTmpl, typename Word, int
+ WordLength >
+class md5_special_op
+{
+ TernaryFuncTmpl<Word> f_;
+ left_rotator<Word, WordLength> lr_;
+
+public:
+ void operator ()(Word &a, Word b, Word c, Word d, Word xk, int s, Word ti)
+ {
+ a = b + this->lr_( a + this->f_(b, c, d) + xk + ti, s );
+ }
+};
+
+// Fill valarray with spaced indices
+std::valarray<std::size_t>
+skipped_indices( std::size_t total_size, std::size_t first_index, std::size_t
+ spacing )
+{
+ BOOST_ASSERT( (total_size > 0u) && (first_index < total_size) &&
+ boost::math::gcd(spacing, total_size) == 1u );
+
+ std::valarray<std::size_t> r( spacing, total_size );
+
+ r[ 0 ] = first_index;
+ std::partial_sum( &r[0], &r[0] + total_size, &r[0] );
+ r %= total_size;
+
+ return r;
+}
+
+// The exact amount of bits needed to be in the queue so an appendage of the
+// length will exactly fill the queue.
+std::size_t const padding_remainder =
+ boost::coding::md5_computer::bits_per_block -
+ boost::coding::md5_computer::significant_bits_per_length;
+
+} // unnamed namespace
 
 
 namespace boost
@@ -56,16 +216,227 @@
 
 // MD5 message-digest computer class-static member definitions -------------//
 
-int const md5_computer::significant_bits_per_length;
-
 std::size_t const md5_computer::words_per_block;
+
+int const md5_computer::significant_bits_per_length;
 std::size_t const md5_computer::bits_per_block;
 
+array<md5_digest::word_type, 64> const md5_computer::hashing_table = { {
+ 0xD76AA478ul,
+ 0xE8C7B756ul,
+ 0x242070DBul,
+ 0xC1BDCEEEul,
+ 0xF57C0FAFul,
+ 0x4787C62Aul,
+ 0xA8304613ul,
+ 0xFD469501ul,
+ 0x698098D8ul,
+ 0x8B44F7AFul,
+ 0xFFFF5BB1ul,
+ 0x895CD7BEul,
+ 0x6B901122ul,
+ 0xFD987193ul,
+ 0xA679438Eul,
+ 0x49B40821ul,
+ 0xF61E2562ul,
+ 0xC040B340ul,
+ 0x265E5A51ul,
+ 0xE9B6C7AAul,
+ 0xD62F105Dul,
+ 0x2441453ul,
+ 0xD8A1E681ul,
+ 0xE7D3FBC8ul,
+ 0x21E1CDE6ul,
+ 0xC33707D6ul,
+ 0xF4D50D87ul,
+ 0x455A14EDul,
+ 0xA9E3E905ul,
+ 0xFCEFA3F8ul,
+ 0x676F02D9ul,
+ 0x8D2A4C8Aul,
+ 0xFFFA3942ul,
+ 0x8771F681ul,
+ 0x6D9D6122ul,
+ 0xFDE5380Cul,
+ 0xA4BEEA44ul,
+ 0x4BDECFA9ul,
+ 0xF6BB4B60ul,
+ 0xBEBFBC70ul,
+ 0x289B7EC6ul,
+ 0xEAA127FAul,
+ 0xD4EF3085ul,
+ 0x4881D05ul,
+ 0xD9D4D039ul,
+ 0xE6DB99E5ul,
+ 0x1FA27CF8ul,
+ 0xC4AC5665ul,
+ 0xF4292244ul,
+ 0x432AFF97ul,
+ 0xAB9423A7ul,
+ 0xFC93A039ul,
+ 0x655B59C3ul,
+ 0x8F0CCC92ul,
+ 0xFFEFF47Dul,
+ 0x85845DD1ul,
+ 0x6FA87E4Ful,
+ 0xFE2CE6E0ul,
+ 0xA3014314ul,
+ 0x4E0811A1ul,
+ 0xF7537E82ul,
+ 0xBD3AF235ul,
+ 0x2AD7D2BBul,
+ 0xEB86D391ul
+} };
+
 // Initial values of the MD buffer, taken from RFC 1321, section 3.3. (Note
 // that the RFC lists each number low-order byte first, while numbers need to be
 // written high-order byte first in C++.)
 md5_computer::ibuffer_type const md5_computer::initial_buffer_ = {
- {0x67452301ul, 0xEFCDAB89ul, 0x98BACDFEul, 0x10325476ul} };
+ {0x67452301ul, 0xEFCDAB89ul, 0x98BADCFEul, 0x10325476ul} };
+
+
+// MD5 message-digest computer class-static member function definitions ----//
+
+// Generated copy of "hashing_table"; see header for notes
+array<md5_digest::word_type, 64>
+md5_computer::generate_hashing_table()
+{
+ array<md5_digest::word_type, 64> r;
+
+ for ( int i = 0 ; i < 64 ; ++i )
+ {
+ double x = std::ldexp( std::abs(std::sin( static_cast<double>(i + 1)
+ )), +32 ); // 2**32 * abs(sin(I)), where I = i + 1
+
+ std::modf( x, &x ); // x -> x rounded towards zero
+ r[ i ] = static_cast<unsigned long>( x );
+ }
+ return r;
+}
+
+
+// MD5 message-digest computer implementation member function definitions --//
+
+// Hash an entire block into the running checksum, using RFC 1321, section 3.4
+void
+md5_computer::update_hash()
+{
+ using std::size_t;
+
+ // Convert the queued bit block to a word block
+ std::valarray<self_type::iword_type> x( self_type::words_per_block ),
+ xx( x ); // setup for later
+
+ for ( size_t i = 0u ; i < self_type::words_per_block ; ++i )
+ {
+ // Use the default inner-product; since "unbuffered_" has "bool"
+ // elements, which convert to 0 or 1, multiplication acts as AND; since
+ // "order_in_word" has distinct single-bit values, addition acts as OR
+ x[ i ] = std::inner_product( order_in_word.begin(), order_in_word.end(),
+ this->unbuffered_.begin() + (i * md5_digest::bits_per_word),
+ self_type::iword_type(0u) );
+ }
+
+ // Set up rounds
+ self_type::ibuffer_type buffer = this->buffer_;
+
+ // Round 1
+ {
+ md5_special_op<md5_f, self_type::iword_type, md5_digest::bits_per_word>
+ ff;
+
+ xx = x[ skipped_indices(self_type::words_per_block, 0, 1) ];
+ for ( size_t i = 0u ; i < self_type::words_per_block ; ++i )
+ {
+ ff( buffer[( 16u - i ) % 4u], buffer[( 17u - i ) % 4u],
+ buffer[( 18u - i ) % 4u], buffer[( 19u - i ) % 4u], xx[i],
+ md5_s[0][i % 4u], self_type::hashing_table[i] );
+ }
+ }
+
+ // Round 2
+ {
+ md5_special_op<md5_g, self_type::iword_type, md5_digest::bits_per_word>
+ gg;
+
+ xx = x[ skipped_indices(self_type::words_per_block, 1, 5) ];
+ for ( size_t i = 0u ; i < self_type::words_per_block ; ++i )
+ {
+ gg( buffer[( 16u - i ) % 4u], buffer[( 17u - i ) % 4u],
+ buffer[( 18u - i ) % 4u], buffer[( 19u - i ) % 4u], xx[i],
+ md5_s[1][i % 4u], self_type::hashing_table[16 + i] );
+ }
+ }
+
+ // Round 3
+ {
+ md5_special_op<md5_h, self_type::iword_type, md5_digest::bits_per_word>
+ hh;
+
+ xx = x[ skipped_indices(self_type::words_per_block, 5, 3) ];
+ for ( size_t i = 0u ; i < self_type::words_per_block ; ++i )
+ {
+ hh( buffer[( 16u - i ) % 4u], buffer[( 17u - i ) % 4u],
+ buffer[( 18u - i ) % 4u], buffer[( 19u - i ) % 4u], xx[i],
+ md5_s[2][i % 4u], self_type::hashing_table[32 + i] );
+ }
+ }
+
+ // Round 4
+ {
+ md5_special_op<md5_i, self_type::iword_type, md5_digest::bits_per_word>
+ ii;
+
+ xx = x[ skipped_indices(self_type::words_per_block, 0, 7) ];
+ for ( size_t i = 0u ; i < self_type::words_per_block ; ++i )
+ {
+ ii( buffer[( 16u - i ) % 4u], buffer[( 17u - i ) % 4u],
+ buffer[( 18u - i ) % 4u], buffer[( 19u - i ) % 4u], xx[i],
+ md5_s[3][i % 4u], self_type::hashing_table[48 + i] );
+ }
+ }
+
+ // Update buffer
+ for ( size_t i = 0u ; i < md5_digest::words_per_digest ; ++i )
+ {
+ this->buffer_[ i ] += buffer[ i ];
+ this->buffer_[ i ] &= low_bits_mask_t< md5_digest :: bits_per_word > ::
+ sig_bits;
+ }
+}
+
+
+// MD5 message-digest computation digest-output member function definition -//
+
+// Check-sum computation; see header for notes
+md5_computer::value_type
+md5_computer::checksum() const
+{
+ // As explained in RFC 1321, section 3, the final check-sum is the state of
+ // the hash after padding and the original length are appended to the
+ // message data. (The padding is sized such that the length's bits always
+ // exactly finishes a block.) The padding is one mandatory TRUE value
+ // followed by the appropriate amount of FALSE values.
+ //
+ // Technically, the modulo-bits_per_block is needed only if bits_unbuffered
+ // exceeds padding_remainder (when it doesn't, just do the subtraction
+ // directly), but this way always works and a conditional is avoided. (The
+ // corresponding test added cases to make sure the old conditional was hit.)
+ self_type cache( *this );
+
+ cache.process_bit( true );
+ cache.process_bit_copies( false, (padding_remainder +
+ self_type::bits_per_block - cache.bits_unbuffered()) %
+ self_type::bits_per_block );
+ cache.process_double_word( this->bits_read() );
+ BOOST_ASSERT( cache.bits_unbuffered() == 0u );
+
+ self_type::buffer_type const rb = cache.last_buffer();
+ self_type::value_type r;
+
+ std::copy( rb.begin(), rb.end(), r.hash );
+ return r;
+}
 
 
 } // namespace coding

Modified: sandbox/md5/libs/coding/test/md5_computer_test.cpp
==============================================================================
--- sandbox/md5/libs/coding/test/md5_computer_test.cpp (original)
+++ sandbox/md5/libs/coding/test/md5_computer_test.cpp 2008-06-20 02:37:52 EDT (Fri, 20 Jun 2008)
@@ -9,17 +9,41 @@
 
 #include <boost/coding/md5.hpp>
 
+#include <boost/array.hpp> // for boost::array
+#include <boost/cstdint.hpp> // for uint_least(8|32|64)_t
+#include <boost/foreach.hpp> // for BOOST_FOREACH
+#include <boost/lexical_cast.hpp> // for boost::lexical_cast
 #include <boost/test/unit_test.hpp> // unit testing framework
 
+#include <algorithm> // for std::reverse, for_each
+#include <climits> // for CHAR_BIT
+#include <cstddef> // for std::size_t
+
 
 #pragma mark Intro stuff
 
 // Put any using-ed types & templates, and typedefs here
+using boost::array;
+using boost::coding::md5_computer;
+using boost::coding::md5_digest;
+
+typedef array<bool, md5_computer::bits_per_block> bit_queue;
 
 
 // Put custom types/templates and helper functions here
 namespace
 {
+
+// Sample MD buffer values
+md5_computer::buffer_type const md5_initial = { {0x67452301ul, 0xEFCDAB89ul,
+ 0x98BADCFEul, 0x10325476ul} }; // from RFC 1321, section 3.3
+md5_computer::buffer_type const md5_empty = { {0xD98C1DD4ul, 0x04B2008Ful,
+ 0x980980E9ul, 0x7E42F8ECul} }; // from RFC 1321, section A.5, result 1
+
+// Sample MD values
+md5_digest const md5_empty_data = { {0xD98C1DD4ul, 0x04B2008Ful, 0x980980E9ul,
+ 0x7E42F8ECul} }; // see md5_empty
+
 } // unnamed namespace
 
 
@@ -32,44 +56,503 @@
 // Test the operations of the MD5 computation class and function
 BOOST_AUTO_TEST_SUITE( md5_computer_suite )
 
-// Construction and inspector test
-BOOST_AUTO_TEST_CASE( md5_computer_constructor_status_test )
+// Default construction test and inspectors (no submission)
+BOOST_AUTO_TEST_CASE( md5_computer_default_constructor_status_test )
+{
+ md5_computer c;
+ BOOST_CHECK_EQUAL( c.bits_read(), 0u );
+ BOOST_CHECK_EQUAL( c.bits_unbuffered(), 0u );
+
+ md5_computer::buffer_type const b = c.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b.begin(), b.end(), md5_initial.begin(),
+ md5_initial.end() );
+
+ bit_queue bq = { {false} };
+ BOOST_CHECK_EQUAL( bq.begin(), c.copy_unbuffered(bq.begin()) );
+}
+
+// Basic bit/byte-submission tests and inspectors (no hashing)
+BOOST_AUTO_TEST_CASE( md5_computer_simple_submission_test )
 {
+ using std::size_t;
+
+ bit_queue scratch;
+
+ // Single bit
+ {
+ md5_computer c1;
+ c1.process_bit( true );
+ BOOST_CHECK_EQUAL( c1.bits_read(), 1u );
+ BOOST_CHECK_EQUAL( c1.bits_unbuffered(), 1u );
+
+ md5_computer::buffer_type const b1 = c1.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b1.begin(), b1.end(),
+ md5_initial.begin(), md5_initial.end() );
+
+ array<bool, 1> const r1 = { {true} };
+ BOOST_CHECK_EQUAL_COLLECTIONS( scratch.begin(),
+ c1.copy_unbuffered(scratch.begin()), r1.begin(), r1.end() );
+ }
+
+ {
+ md5_computer c2;
+ c2.process_bit( false );
+ BOOST_CHECK_EQUAL( c2.bits_read(), 1u );
+ BOOST_CHECK_EQUAL( c2.bits_unbuffered(), 1u );
+
+ md5_computer::buffer_type const b2 = c2.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b2.begin(), b2.end(),
+ md5_initial.begin(), md5_initial.end() );
+
+ array<bool, 1> const r2 = { {false} };
+ BOOST_CHECK_EQUAL_COLLECTIONS( scratch.begin(),
+ c2.copy_unbuffered(scratch.begin()), r2.begin(), r2.end() );
+ }
+
+ // Multiple bits, under a byte
+ {
+ md5_computer c3;
+ size_t const bits_to_use = 7u; // only 6 will be necessary
+ unsigned char const value = 0x37u; // 0110111
+ c3.process_bits( value, bits_to_use );
+ BOOST_CHECK_EQUAL( c3.bits_read(), bits_to_use );
+ BOOST_CHECK_EQUAL( c3.bits_unbuffered(), bits_to_use );
+
+ md5_computer::buffer_type const b3 = c3.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b3.begin(), b3.end(),
+ md5_initial.begin(), md5_initial.end() );
+
+ array<bool, 7> const r3 = { {false, true, true, false, true, true,
+ true} };
+ BOOST_CHECK_EQUAL_COLLECTIONS( scratch.begin(),
+ c3.copy_unbuffered(scratch.begin()), r3.begin(), r3.end() );
+ }
+
+ {
+ md5_computer c4;
+ size_t const bits_to_use = 6u;
+ c4.process_bit_copies( false, bits_to_use );
+ BOOST_CHECK_EQUAL( c4.bits_read(), bits_to_use );
+ BOOST_CHECK_EQUAL( c4.bits_unbuffered(), bits_to_use );
+
+ md5_computer::buffer_type const b4 = c4.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b4.begin(), b4.end(),
+ md5_initial.begin(), md5_initial.end() );
+
+ array<bool, 6> const r4 = {{false, false, false, false, false, false}};
+ BOOST_CHECK_EQUAL_COLLECTIONS( scratch.begin(),
+ c4.copy_unbuffered(scratch.begin()), r4.begin(), r4.end() );
+ }
+
+ {
+ md5_computer c5;
+ size_t const bits_to_use = 5u;
+ c5.process_bit_copies( true, bits_to_use );
+ BOOST_CHECK_EQUAL( c5.bits_read(), bits_to_use );
+ BOOST_CHECK_EQUAL( c5.bits_unbuffered(), bits_to_use );
+
+ md5_computer::buffer_type const b5 = c5.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b5.begin(), b5.end(),
+ md5_initial.begin(), md5_initial.end() );
+
+ array<bool, 5> const r5 = { {true, true, true, true, true} };
+ BOOST_CHECK_EQUAL_COLLECTIONS( scratch.begin(),
+ c5.copy_unbuffered(scratch.begin()), r5.begin(), r5.end() );
+ }
+
+ // Whole bytes, one value
+ {
+ md5_computer c6;
+ unsigned char const value = 0x37u; // {'0' x (CHAR_BIT-6)}110111
+ c6.process_byte( value );
+ BOOST_CHECK_EQUAL( c6.bits_read(), CHAR_BIT );
+ BOOST_CHECK_EQUAL( c6.bits_unbuffered(), CHAR_BIT );
+
+ md5_computer::buffer_type const b6 = c6.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b6.begin(), b6.end(),
+ md5_initial.begin(), md5_initial.end() );
+
+ // The usual initialization routine won't work because there will be an
+ // unknown number of "false" entries in the beginning. (Can't assume
+ // that CHAR_BIT is 8.) But C++'s rules state that incomplete array
+ // initialization lists are filled with the default value (i.e.
+ // "false") at the trailing unspecified entries. So specify "r6"
+ // backwards (i.e. lowest bit first), then reverse it!
+ array<bool, CHAR_BIT> r6 = { {true, true, true, false, true, true} };
+ std::reverse( r6.begin(), r6.end() );
+ BOOST_CHECK_EQUAL_COLLECTIONS( scratch.begin(),
+ c6.copy_unbuffered(scratch.begin()), r6.begin(), r6.end() );
+ }
+
+ {
+ md5_computer c7;
+ size_t const bytes_to_use = 3u;
+ unsigned char const value = 0x37u; // {'0' x (CHAR_BIT-6)}110111
+ BOOST_REQUIRE( CHAR_BIT * bytes_to_use < md5_computer::bits_per_block );
+ c7.process_byte_copies( value, bytes_to_use );
+ BOOST_CHECK_EQUAL( c7.bits_read(), CHAR_BIT * bytes_to_use );
+ BOOST_CHECK_EQUAL( c7.bits_unbuffered(), CHAR_BIT * bytes_to_use );
+
+ md5_computer::buffer_type const b7 = c7.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b7.begin(), b7.end(),
+ md5_initial.begin(), md5_initial.end() );
+
+ array<bool, 3 * CHAR_BIT> r7;
+ for ( size_t i = 0u ; i < 3 * CHAR_BIT ; ++i )
+ {
+ // Copy "value" thrice bit-wise, highest bit first
+ r7[ i ] = value & ( 0x01u << (CHAR_BIT - 1 - ( i % CHAR_BIT )) );
+ }
+ BOOST_CHECK_EQUAL_COLLECTIONS( scratch.begin(),
+ c7.copy_unbuffered(scratch.begin()), r7.begin(), r7.end() );
+ }
+
+ // Byte buffers
+ {
+ // Two pointers
+ md5_computer c8;
+ array<unsigned char, 3> const values = { {0x37u, 0x48u, 0x1Cu} };
+ size_t const bytes_to_use = 3u;
+ BOOST_REQUIRE( CHAR_BIT * bytes_to_use < md5_computer::bits_per_block );
+ c8.process_block( values.begin(), values.end() );
+ BOOST_CHECK_EQUAL( c8.bits_read(), CHAR_BIT * bytes_to_use );
+ BOOST_CHECK_EQUAL( c8.bits_unbuffered(), CHAR_BIT * bytes_to_use );
+
+ md5_computer::buffer_type const b8 = c8.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b8.begin(), b8.end(),
+ md5_initial.begin(), md5_initial.end() );
+
+ array<bool, 3 * CHAR_BIT> r8;
+ for ( size_t i = 0u ; i < 3 ; ++i )
+ {
+ for ( size_t j = 0u ; j < CHAR_BIT ; ++j )
+ {
+ // Copy "values", each time highest bit first
+ r8[ i * CHAR_BIT + j ] = values[ i ] & ( 0x01u << (CHAR_BIT - 1
+ - j) );
+ }
+ }
+ BOOST_CHECK_EQUAL_COLLECTIONS( scratch.begin(),
+ c8.copy_unbuffered(scratch.begin()), r8.begin(), r8.end() );
+
+ // Pointer and length
+ md5_computer c9;
+ c9.process_bytes( values.begin(), bytes_to_use );
+ BOOST_CHECK_EQUAL( c9.bits_read(), CHAR_BIT * bytes_to_use );
+ BOOST_CHECK_EQUAL( c9.bits_unbuffered(), CHAR_BIT * bytes_to_use );
+
+ md5_computer::buffer_type const b9 = c9.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b9.begin(), b9.end(),
+ md5_initial.begin(), md5_initial.end() );
+
+ BOOST_CHECK_EQUAL_COLLECTIONS( scratch.begin(),
+ c9.copy_unbuffered(scratch.begin()), r8.begin(), r8.end() );
+ }
+
+ // Octets, words, and double-words
+ {
+ md5_computer c10;
+ boost::uint_least8_t const value = 0x37u; // 00110111
+ c10.process_octet( value );
+ BOOST_CHECK_EQUAL( c10.bits_read(), 8 );
+ BOOST_CHECK_EQUAL( c10.bits_unbuffered(), 8 );
+
+ md5_computer::buffer_type const b10 = c10.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b10.begin(), b10.end(),
+ md5_initial.begin(), md5_initial.end() );
+
+ array<bool, 8> r10 = { {false, false, true, true, false, true, true,
+ true} };
+ BOOST_CHECK_EQUAL_COLLECTIONS( scratch.begin(),
+ c10.copy_unbuffered(scratch.begin()), r10.begin(), r10.end() );
+ }
+
+ {
+ md5_computer c11;
+ boost::uint_least32_t const value = 0xDEAC0812ul;
+ // 11011110101011000000100000010010
+ c11.process_word( value );
+ BOOST_CHECK_EQUAL( c11.bits_read(), 32 );
+ BOOST_CHECK_EQUAL( c11.bits_unbuffered(), 32 );
+
+ md5_computer::buffer_type const b11 = c11.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b11.begin(), b11.end(),
+ md5_initial.begin(), md5_initial.end() );
+
+ array<bool, 32> r11 = { {false, false, false, true, false, false, true,
+ false, false, false, false, false, true, false, false, false, true,
+ false, true, false, true, true, false, false, true, true, false, true,
+ true, true, true, false} }; // 12, 08, AC, DE
+ BOOST_CHECK_EQUAL_COLLECTIONS( scratch.begin(),
+ c11.copy_unbuffered(scratch.begin()), r11.begin(), r11.end() );
+ }
+
+ {
+ md5_computer c12;
+ boost::uint_least64_t const value = 0x01234567FEDCBA98ull;
+ // 0000000100100011010001010110011111111110110111001011101010011000
+ c12.process_double_word( value );
+ BOOST_CHECK_EQUAL( c12.bits_read(), 64 );
+ BOOST_CHECK_EQUAL( c12.bits_unbuffered(), 64 );
+
+ md5_computer::buffer_type const b12 = c12.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b12.begin(), b12.end(),
+ md5_initial.begin(), md5_initial.end() );
+
+ array<bool, 64> r12 = { {true, false, false, true, true, false, false,
+ false, true, false, true, true, true, false, true, false, true, true,
+ false, true, true, true, false, false, true, true, true, true, true,
+ true, true, false, false, true, true, false, false, true, true, true,
+ false, true, false, false, false, true, false, true, false, false,
+ true, false, false, false, true, true, false, false, false, false,
+ false, false, false, true} }; // 98, BA, DC, FE; 67, 45, 23, 01
+ BOOST_CHECK_EQUAL_COLLECTIONS( scratch.begin(),
+ c12.copy_unbuffered(scratch.begin()), r12.begin(), r12.end() );
+ }
+}
+
+// Hashed bit/byte-submission tests and inspectors
+BOOST_AUTO_TEST_CASE( md5_computer_hashed_submission_test )
+{
+ // Confirm quality of math library
+ array<md5_digest::word_type, 64> const hash_table =
+ md5_computer::generate_hashing_table();
+ BOOST_WARN_EQUAL_COLLECTIONS( hash_table.begin(), hash_table.end(),
+ md5_computer::hashing_table.begin(), md5_computer::hashing_table.end() );
+
+ // This simulates running MD5 on an empty message
+ md5_computer c;
+ c.process_bit( true );
+ c.process_bit_copies( false, md5_computer::bits_per_block -
+ md5_computer::significant_bits_per_length - 1u );
+ c.process_double_word( 0ull );
+ BOOST_CHECK_EQUAL( c.bits_read(), md5_computer::bits_per_block );
+ BOOST_CHECK_EQUAL( c.bits_unbuffered(), 0u );
+
+ md5_computer::buffer_type const b = c.last_buffer();
+ BOOST_CHECK_EQUAL_COLLECTIONS( b.begin(), b.end(), md5_empty.begin(),
+ md5_empty.end() );
+
+ bit_queue scratch;
+ BOOST_CHECK_EQUAL( scratch.begin(), c.copy_unbuffered(scratch.begin()) );
 }
 
 // Equality operators test
 BOOST_AUTO_TEST_CASE( md5_computer_equality_test )
 {
+ md5_computer c1, c2, c3, c4;
+
+ // Make some non-default, something like an empty MD5
+ c3.process_bit( true );
+ c3.process_bit_copies( false, md5_computer::bits_per_block -
+ md5_computer::significant_bits_per_length - 1u );
+ c3.process_double_word( 0ull );
+
+ c4.process_bit( true );
+ c4.process_bit_copies( false, md5_computer::bits_per_block -
+ md5_computer::significant_bits_per_length - 1u );
+ c4.process_double_word( 0ull );
+
+ // Compare
+ BOOST_CHECK( c1 == c1 ); BOOST_CHECK( c1 == c2 ); BOOST_CHECK( c1 != c3 );
+ BOOST_CHECK( c1 != c4 );
+ BOOST_CHECK( c2 == c1 ); BOOST_CHECK( c2 == c2 ); BOOST_CHECK( c2 != c3 );
+ BOOST_CHECK( c2 != c4 );
+ BOOST_CHECK( c3 != c1 ); BOOST_CHECK( c3 != c2 ); BOOST_CHECK( c3 == c3 );
+ BOOST_CHECK( c3 == c4 );
+ BOOST_CHECK( c4 != c1 ); BOOST_CHECK( c4 != c2 ); BOOST_CHECK( c4 == c3 );
+ BOOST_CHECK( c4 == c4 );
+
+ // Make the only difference at the hash level
+ c2.process_bit( true );
+ c2.process_bit_copies( false, md5_computer::bits_per_block -
+ md5_computer::significant_bits_per_length - 1u );
+ c2.process_double_word( 1ull ); // one bit differs
+ BOOST_REQUIRE_EQUAL( c2.bits_read(), c3.bits_read() );
+ BOOST_REQUIRE_EQUAL( c2.bits_unbuffered(), 0u );
+ BOOST_CHECK( c2 != c3 );
+
+ // Make the only difference at the queue level
+ c3.process_bit( true );
+ c4.process_bit( false );
+ BOOST_REQUIRE_EQUAL( c3.bits_read(), c4.bits_read() );
+ BOOST_REQUIRE( c3.last_buffer() == c4.last_buffer() );
+ BOOST_CHECK( c3 != c4 );
 }
 
-// Output archiving test
-BOOST_AUTO_TEST_CASE( md5_computer_output_test )
+// Copy construction and assignments test
+BOOST_AUTO_TEST_CASE( md5_computer_copy_constructor_status_test )
 {
+ // Get a non-default valued computer, something like an empty MD5
+ md5_computer c1;
+ bit_queue scratch;
+
+ c1.process_bit( true );
+ c1.process_bit_copies( false, md5_computer::bits_per_block -
+ md5_computer::significant_bits_per_length - 1u );
+ c1.process_double_word( 0ull );
+ c1.process_bit( false ); // one extra after buffer hash
+ BOOST_CHECK( c1.bits_read() != c1.bits_unbuffered() );
+ BOOST_CHECK( c1.last_buffer() == md5_empty );
+ BOOST_CHECK_EQUAL(1, c1.copy_unbuffered(scratch.begin()) - scratch.begin());
+
+ // Copy constructor
+ md5_computer c2, c3( c1 );
+
+ BOOST_CHECK( c3 == c1 );
+ BOOST_CHECK( c3 != c2 );
+
+ // Assignment operator
+ c3 = c2;
+ BOOST_CHECK( c3 == c2 );
+ BOOST_CHECK( c3 != c1 );
+
+ // Assignment method
+ c3.assign( c1 );
+ BOOST_CHECK( c3 == c1 );
+ BOOST_CHECK( c3 != c2 );
+
+ // Reset to default
+ c3.reset();
+ BOOST_CHECK( c3 == c2 );
+ BOOST_CHECK( c3 != c1 );
+
+ // Swap
+ c3.swap( c1 );
+ BOOST_CHECK( c3 != c2 );
+ BOOST_CHECK( c1 == c2 );
+
+ // Free-function swap
+ boost::coding::swap( c1, c3 );
+ BOOST_CHECK( c3 == c2 );
+ BOOST_CHECK( c1 != c2 );
 }
 
-// Input archiving test
-BOOST_AUTO_TEST_CASE( md5_computer_input_test )
+// Checksum computation test
+BOOST_AUTO_TEST_CASE( md5_computer_checksum_test )
 {
+ using boost::lexical_cast;
+
+ // Empty MD5 message
+ md5_computer c1;
+ md5_digest const s1 = c1.checksum();
+
+ BOOST_CHECK_EQUAL( s1, md5_empty_data );
+
+ // Application operator interface
+ md5_digest const s2 = c1();
+ BOOST_CHECK_EQUAL( s1, s2 );
+
+ // Non-empty message below the limit of padding wrap-around
+ c1.process_octet( 0x61u ); // 'a' in ASCII
+ BOOST_REQUIRE( c1.bits_unbuffered() > 0u );
+ BOOST_REQUIRE( c1.bits_unbuffered() < md5_computer::bits_per_block -
+ md5_computer::significant_bits_per_length );
+ BOOST_CHECK_EQUAL( c1.checksum(),
+ lexical_cast<md5_digest>("0cc175b9c0f1b6a831c399e269772661") );
+
+ // Non-empty message above the limit of padding wrap-around
+ c1.reset();
+ for ( unsigned i = 65 ; i <= 90 ; ++i ) c1.process_octet( i );
+ for ( unsigned i = 97 ; i <= 122 ; ++i ) c1.process_octet( i );
+ for ( unsigned i = 48 ; i <= 57 ; ++i ) c1.process_octet( i );
+ // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" in
+ // ASCII
+ BOOST_REQUIRE( c1.bits_unbuffered() > md5_computer::bits_per_block -
+ md5_computer::significant_bits_per_length );
+ BOOST_CHECK_EQUAL( c1.checksum(),
+ lexical_cast<md5_digest>("d174ab98d277d9f5a5611c2c9f419d9f") );
 }
 
-// Bit-wise computation test
+// Bit-wise function-object computation test
 BOOST_AUTO_TEST_CASE( md5_computer_bit_input_test )
 {
+ using std::for_each;
+
+ // Compare straight usage
+ md5_computer c1, c2;
+ unsigned char const value = 0x37u;
+ array<bool, 8> const value_bits = { {false, false, true, true, false, true,
+ true, true} };
+
+ c1.process_octet( value ); // 00110111
+ BOOST_FOREACH( bool b, value_bits )
+ c2.bits( b );
+ BOOST_CHECK( c1 == c2 );
+
+ // Use with algorithms, note that it works by reference!
+ md5_computer c3, c4;
+
+ BOOST_CHECK( c3 == c4 );
+ for_each( value_bits.begin(), value_bits.end(), c3.bits );
+ BOOST_CHECK( c3 != c4 );
+ BOOST_CHECK( c3 == c2 );
+
+ // Assignment
+ c4.bits = c3.bits;
+ BOOST_CHECK( c4 == c3 );
+
+ // Just a formality
+ c4.reset();
+ BOOST_CHECK( c4 != c3 );
+ c4.bits = for_each( value_bits.begin(), value_bits.end(), c4.bits );
+ BOOST_CHECK( c4 == c3 );
 }
 
-// Byte-wise computation test
+// Byte-wise function-object computation test
 BOOST_AUTO_TEST_CASE( md5_computer_byte_input_test )
 {
-}
+ using std::for_each;
 
-// Checksum computation test
-BOOST_AUTO_TEST_CASE( md5_computer_checksum_test )
-{
+ // Compare straight usage
+ md5_computer c1, c2;
+ array<unsigned char, 3> const values = { {0x37u, 0x48u, 0x1Cu} };
+
+ c1.process_bytes( values.begin(), values.size() );
+ BOOST_FOREACH( unsigned char b, values )
+ c2.bytes( b );
+ BOOST_CHECK( c1 == c2 );
+
+ // Use with algorithms, note that it works by reference!
+ md5_computer c3, c4;
+
+ BOOST_CHECK( c3 == c4 );
+ for_each( values.begin(), values.end(), c3.bytes );
+ BOOST_CHECK( c3 != c4 );
+ BOOST_CHECK( c3 == c2 );
+
+ // Assignment
+ c4.bytes = c3.bytes;
+ BOOST_CHECK( c4 == c3 );
+
+ // Just a formality
+ c4.reset();
+ BOOST_CHECK( c4 != c3 );
+ c4.bytes = for_each( values.begin(), values.end(), c4.bytes );
+ BOOST_CHECK( c4 == c3 );
 }
 
+// Output archiving test
+//BOOST_AUTO_TEST_CASE( md5_computer_output_test )
+//{
+//}
+
+// Input archiving test
+//BOOST_AUTO_TEST_CASE( md5_computer_input_test )
+//{
+//}
+
 // Single-call function test
 BOOST_AUTO_TEST_CASE( compute_md5_test )
 {
+ using boost::coding::compute_md5;
+
+ BOOST_CHECK_EQUAL( compute_md5(0, 0), md5_empty_data );
+
+ // How do we get consistent data when we can't count on CHAR_BIT being the
+ // same everywhere? Do we just screw over testers without 8-bit bytes or
+ // ones without ASCII (or superset)? If so, then we could use the other
+ // results from RFC 1321, section A.5.
 }
 
 BOOST_AUTO_TEST_SUITE_END()


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk