// bdlde_crc32.h                                                      -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BDLDE_CRC32
#define INCLUDED_BDLDE_CRC32

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a mechanism for computing the CRC-32 checksum of a dataset.
//
//@CLASSES:
//  bdlde::Crc32: stores and updates a CRC-32 checksum
//
//@SEE_ALSO:
//
//@DESCRIPTION: This component implements a mechanism for computing, updating,
// and streaming a CRC-32 checksum (a cyclic redundancy check comprised of 32
// bits).  This checksum is a strong and fast technique for determining whether
// or not a message was received without errors.  Note that a CRC-32 checksum
// does not aid in error correction and is not naively useful in any sort of
// cryptographic application.  Compared to other methods such as MD5 and
// SHA-256, it is relatively easy to find alternate texts with identical
// checksum.
//
///Usage
///-----
// The following snippets of code illustrate a typical use of the
// 'bdlde::Crc32' class.  Each function would typically execute in separate
// processes or potentially on separate machines.  The 'senderExample' function
// below demonstrates how a message sender can write a message and its CRC-32
// checksum to a 'bdex' output stream.  Note that 'Out' may be a 'typedef' of
// any class that implements the 'bslx::OutStream' protocol:
//..
//  void senderExample(Out& output)
//      // Write a message and its CRC-32 checksum to the specified 'output'
//      // stream.
//  {
//      // prepare a message
//      bsl::string message = "This is a test message.";
//
//      // generate a checksum for 'message'
//      bdlde::Crc32 crc(message.data(), message.length());
//
//      // write the message to 'output'
//      output << message;
//
//      // write the checksum to 'output'
//      const int VERSION = 1;
//      crc.bdexStreamOut(output, VERSION);
//  }
//..
// The 'receiverExample' function below illustrates how a message receiver can
// read a message and its CRC-32 checksum from a 'bdex' input stream, then
// perform a local CRC-32 computation to verify that the message was received
// intact.  Note that 'In' may be a 'typedef' of any class that implements the
// 'bslx::InStream' protocol:
//..
//  void receiverExample(In& input)
//      // Read a message and its CRC-32 checksum from the specified 'input'
//      // stream, and verify the integrity of the message.
//  {
//      // read the message from 'input'
//      bsl::string message;
//      input >> message;
//
//      // read the checksum from 'input'
//      bdlde::Crc32 crc;
//      const int VERSION = 1;
//      crc.bdexStreamIn(input, VERSION);
//
//      // locally compute the checksum of the received 'message'
//      bdlde::Crc32 crcLocal;
//      crcLocal.update(message.data(), message.length());
//
//      // verify that the received and locally-computed checksums match
//      assert(crcLocal == crc);
//  }
//..

#include <bdlscm_version.h>

#include <bsls_assert.h>

#include <bsl_cstddef.h>
#include <bsl_iosfwd.h>

namespace BloombergLP {
namespace bdlde {

                                // ===========
                                // class Crc32
                                // ===========

class Crc32 {
    // This class represents a CRC-32 checksum value that can be updated as
    // data is provided.
    //
    // More generally, this class supports a complete set of *value*
    // *semantic* operations, including copy construction, assignment,
    // equality comparison, 'ostream' printing, and 'bdex' serialization.
    // (A precise operational definition of when two objects have the same
    // value can be found in the description of 'operator==' for the class.)
    // This class is *exception* *neutral* with no guarantee of rollback:
    // if an exception is thrown during the invocation of a method on a
    // pre-existing object, the class is left in a valid state, but its value
    // is undefined.  In no event is memory leaked.  Finally, *aliasing* (e.g.,
    // using all or part of an object as both source and destination) is
    // supported in all cases.

    // DATA
    unsigned int d_crc;  // value of the checksum ^ 0xffffffff

    // FRIENDS
    friend bool operator==(const Crc32&, const Crc32&);

  public:
    // CLASS METHODS
    static int maxSupportedBdexVersion(int versionSelector);
        // Return the maximum valid BDEX format version, as indicated by the
        // specified 'versionSelector', to be passed to the 'bdexStreamOut'
        // method.  Note that the 'versionSelector' is expected to be formatted
        // as 'yyyymmdd', a date representation.  See the 'bslx' package-level
        // documentation for more information on BDEX streaming of
        // value-semantic types and containers.

    // CREATORS
    Crc32();
        // Construct a checksum having the value corresponding to no data
        // having been provided (i.e., having the value 0).

    Crc32(const void *data, bsl::size_t length);
        // Construct a checksum corresponding to the specified 'data' having
        // the specified 'length' (in bytes).  Note that if 'data' is 0, then
        // 'length' also must be 0.

    Crc32(const Crc32& original);
        // Construct a checksum having the value of the specified 'original'
        // checksum.

    // ~Crc32();
        // Destroy this checksum.  Note that this trivial destructor is
        // generated by the compiler.

    // MANIPULATORS
    Crc32& operator=(const Crc32& rhs);
        // Assign to this checksum the value of the specified 'rhs' checksum,
        // and return a reference to this modifiable checksum.

    template <class STREAM>
    STREAM& bdexStreamIn(STREAM& stream, int version);
        // Assign to this object the value read from the specified input
        // 'stream' using the specified 'version' format, and return a
        // reference to 'stream'.  If 'stream' is initially invalid, this
        // operation has no effect.  If 'version' is not supported, this object
        // is unaltered and 'stream' is invalidated but otherwise unmodified.
        // If 'version' is supported but 'stream' becomes invalid during this
        // operation, this object has an undefined, but valid, state.  Note
        // that no version is read from 'stream'.  See the 'bslx' package-level
        // documentation for more information on BDEX streaming of
        // value-semantic types and containers.

    unsigned int checksumAndReset();
        // Return the current value of this checksum and set the value of this
        // checksum to the value the default constructor provides.

    void reset();
        // Reset the value of this checksum to the value the default
        // constructor provides.

    void update(const void *data, bsl::size_t length);
        // Update the value of this checksum to incorporate the specified
        // 'data' having the specified 'length'.  If the current state is the
        // default state, the resultant value of this checksum is the
        // application of the CRC-32 algorithm upon the currently given 'data'
        // of the given 'length'.  If this checksum has been previously
        // provided data and has not been subsequently reset, the current state
        // is not the default state and the resultant value is equivalent to
        // applying the CRC-32 algorithm upon the concatenation of all the
        // provided data.  Note that if 'data' is 0, then 'length' also must be
        // 0.

    // ACCESSORS
    template <class STREAM>
    STREAM& bdexStreamOut(STREAM& stream, int version) const;
        // Write this value to the specified output 'stream' using the
        // specified 'version' format, and return a reference to 'stream'.
        // If 'stream' is initially invalid, this operation has no effect.
        // If 'version' is not supported, 'stream' is invalidated but
        // otherwise unmodified.  Note that 'version' is not written to
        // 'stream'.  See the 'bslx' package-level documentation for more
        // information on BDEX streaming of value-semantic types and
        // containers.

    unsigned int checksum() const;
        // Return the current value of this checksum.

    bsl::ostream& print(bsl::ostream& stream) const;
        // Format this object to the specified output 'stream' at the (absolute
        // value of) the optionally specified indentation 'level' and return a
        // reference to 'stream'.  If 'level' is specified, optionally specify
        // 'spacesPerLevel', the number of spaces per indentation level for
        // this and all of its nested objects.  If 'level' is negative,
        // suppress indentation of the first line.  If 'spacesPerLevel' is
        // negative, format the entire output on one line, suppressing all but
        // the initial indentation (as governed by 'level').  If 'stream' is
        // not valid on entry, this operation has no effect.

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
    // CLASS METHOD
    static int maxSupportedBdexVersion();
        // Return the most current 'bdex' streaming version number supported by
        // this class.  (See the package-group-level documentation for more
        // information on 'bdex' streaming of container types.)

    // ACCESSOR
    unsigned int view() const;
        // Return the current value of this checksum.
        //
        // DEPRECATED: use method 'checksum' instead.
#endif // BDE_OMIT_INTERNAL_DEPRECATED

};

// FREE OPERATORS
bool operator==(const Crc32& lhs, const Crc32& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' checksums have the same
    // value, and 'false' otherwise.  Two checksums have the same value if the
    // values obtained from their 'checksum' methods are identical.

bool operator!=(const Crc32& lhs, const Crc32& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' checksums do not have the
    // same value, and 'false' otherwise.  Two checksums do not have the same
    // value if the values obtained from their 'checksum' methods differ.

bsl::ostream& operator<<(bsl::ostream& stream, const Crc32& checksum);
    // Write to the specified output 'stream' the specified 'checksum' value
    // and return a reference to the modifiable 'stream'.

// ============================================================================
//                        INLINE FUNCTION DEFINITIONS
// ============================================================================

                                // -----------
                                // class Crc32
                                // -----------

// CLASS METHODS
inline
int Crc32::maxSupportedBdexVersion(int)
{
    return 1;
}

// CREATORS
inline
Crc32::Crc32()
: d_crc(0xffffffff)
{
}

inline
Crc32::Crc32(const void *data, bsl::size_t length)
: d_crc(0xffffffff)
{
    update(data, length);
}

inline
Crc32::Crc32(const Crc32& original)
: d_crc(original.d_crc)
{
}

// MANIPULATORS
inline
Crc32& Crc32::operator=(const Crc32& rhs)
{
    d_crc = rhs.d_crc;
    return *this;
}

template <class STREAM>
STREAM& Crc32::bdexStreamIn(STREAM& stream, int version)
{
    if (stream) {
        switch (version) {
          case 1: {
            unsigned int crc;
            stream.getUint32(crc);
            if (!stream) {
                return stream;                                        // RETURN
            }
            d_crc = crc;
          } break;
          default: {
            stream.invalidate();
          } break;
        }
    }
    return stream;
}

inline
unsigned int Crc32::checksumAndReset()
{
    const unsigned int crc = d_crc;
    d_crc = 0xffffffff;
    return crc ^ 0xffffffff;
}

inline
void Crc32::reset()
{
    d_crc = 0xffffffff;
}

// ACCESSORS
template <class STREAM>
STREAM& Crc32::bdexStreamOut(STREAM& stream, int version) const
{
    switch (version) {
      case 1: {
        stream.putUint32(d_crc);
      } break;
      default: {
        stream.invalidate();
      } break;
    }
    return stream;
}

inline
unsigned int Crc32::checksum() const
{
    return d_crc ^ 0xffffffff;
}

#ifndef BDE_OMIT_INTERNAL_DEPRECATED

// CLASS METHODS
inline
int Crc32::maxSupportedBdexVersion()
{
    return maxSupportedBdexVersion(0);
}

// ACCESSORS
inline
unsigned int Crc32::view() const
{
    return d_crc ^ 0xffffffff;
}

#endif // BDE_OMIT_INTERNAL_DEPRECATED

}  // close package namespace

// FREE OPERATORS
inline
bool bdlde::operator==(const Crc32& lhs, const Crc32& rhs)
{
    return lhs.d_crc == rhs.d_crc;
}

inline
bool bdlde::operator!=(const Crc32& lhs, const Crc32& rhs)
{
    return !(lhs == rhs);
}

inline
bsl::ostream& bdlde::operator<<(bsl::ostream& stream, const Crc32& checksum)
{
    return checksum.print(stream);
}

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------