// bdldfp_uint128.h                                                   -*-C++-*-
#ifndef INCLUDED_BDLDFP_UINT128
#define INCLUDED_BDLDFP_UINT128

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

//@PURPOSE: Provide a representation of a 128-bit 'int' for bitwise operations.
//
//@CLASSES:
//  bdldfp::Uint128: A representation of a 128-bit unsigned integer
//
//@SEE_ALSO: bsl::bitset
//
//@DESCRIPTION: This component provides a value-semantic type,
// 'bdldfp::Uint128', that is used to represent a 128-bit unsigned integer
// having host-native byte order.  This component also provides a set of useful
// bitwise manipulation operators for this type.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Representing a 128 bit pattern for IPv6
/// - - - - - - - - - - - - - - - - - - - - - - - - -
// Starting in 1996, the world's TCP/IP infrastructure started the move to a
// 128-bit addressing scheme, IPv6.  IPv4 had a useful quality in that it could
// be represented by a single 32-bit machine word for internal routing
// purposes.  With IPv6, the need arises to manipulate 128-bit values
// representing IPv6 addresses for similar routing purposes.
//
// Suppose we need to write a function that needs to take 128-bit addresses
// indicating downstream routers in our network.
//
// First, we forward declare the "addRouter" function that takes an IPv6
// address indicating a router, and an IPv6 network address (a partial IP
// address, with trailing 0's) that the router covers:
//..
//  void addRouter(bdldfp::Uint128 router, bdldfp::Uint128 network);
//..
// Now we create a function that loads a set of networks and routers:
//..
//  void setupNetwork()
//  {
//      bdldfp::Uint128 network;
//      network.setHigh(0x4242000042420000L);
//      network.setLow(0x0);
//
//      bdldfp::Uint128 router(0x000012345678ABCDL, 0xDCBA000087654321L);
//..
// Finally we invoke addRouter, on our network and router.
//..
//      addRouter(router, network);
//  }
//..
// Notice that Uint128 values can be created from high/low pairs.
//
///Example 2: Checking a 128-bit IPv6 Network Mask
///- - - - - - - - - - - - - - - - - - - - - - - -
// In networks, checking a network mask is a fundamental operation.  A 128-bit
// mask can be used to indicate if an address is in a network, and where in the
// network an address is.
//
// Suppose we need to decide if an address is within a network, and extract the
// sub-address from the IPv6 address.
//
// First, we define a function that checks a passed IPv6 address and indicates
// the sub-address, and network membership:
//..
//  bool checkNetworkAddress(bdldfp::Uint128 *subAddress,
//                           bdldfp::Uint128  network,
//                           int              maskLength
//                           bdldfp::Uint128  address)
// {
//..
// Then, we compute a net mask for the specified 'maskLength':
//..
//      bdldfp::Uint128 netMask;
//      for (int i = 0; i < maskLength; ++i) {
//          netMask |= 1;
//          if (i != maskLength - 1) {
//              netMask <<= 1;
//          }
//      }
//..
// Notice that it is possible to shift 'Uint128' values as if they were a
// native type.  Meaning that it is possible to shift a 'Uint128' by 64-bits,
// or more, in single operation, because 'Uint128' functions just like a
// built-in 128-bit integer type would.
//
// Next, we calculate whether the passed address is within the network:
//..
//      bool inNetwork = network == (address & ~netMask);
//..
// Then, we compute the subAddress, if the address is in the network:
//..
//      if (inNetwork) {
//          *subAddress = address & netMask;
//      }
//..
// Now, we return whether the address is in the network, and close the
// function:
//..
//      return inNetwork
//  }
//..
// Finally, we call 'checkNetworkAddress' on a test network and address:
//..
//  bdldfp::Uint128 subAddress;
//  assert(checkNetworkAddress(
//              &subAddress,
//              bdldfp::Uint128(0xABCD424200001234L,0x22FF12345678L),
//              bdldfp::Uint128(0xABCD424200001234L,0x22FF00000000L),
//              32); // The network has a 32-bit internal address mask.
//  assert(subAddress == 0x12345678L);
//..
// Notice that primitive 64-bit words can be promoted to 128-bit addresses.

#include <bsls_types.h>
#include <bsls_platform.h>

namespace BloombergLP {
namespace bdldfp {

                             // =============
                             // class Uint128
                             // =============

class Uint128 {
    // This value-semantic type represents a 128-bit integer, with host machine
    // byte order.

  private:
    // DATA

    // The high and low values are laid out in an architecture dependent
    // fashion to facilitate a native-endian layout.  This class is expected to
    // have standard layout.

    #ifdef BSLS_PLATFORM_IS_BIG_ENDIAN
    bsls::Types::Uint64 d_high;
    bsls::Types::Uint64 d_low;
    #elif defined(BSLS_PLATFORM_IS_LITTLE_ENDIAN)
    bsls::Types::Uint64 d_low;
    bsls::Types::Uint64 d_high;
    #else
    #error Only big or little endian is supported.
    #endif

  public:
    // CREATORS
    Uint128();
        // Create an Uint128 object having the value '0'

    Uint128(bsls::Types::Uint64 initialValue);                      // IMPLICIT
        // Create an 'Uint128' object having the 128-bit integer bit pattern of
        // the value of the specified 'initialValue'.

    Uint128(bsls::Types::Uint64 initialHigh, bsls::Types::Uint64 initialLow);
        // Create an 'Uint128' object having the 128-bit pattern specified by
        // 'initialHigh..initialLow'

    //! ~Uint128() = default;
        // Destroy this object.

    // MANIPULATORS
    //! Uint128& operator=(const Uint128& rhs) = default;
        // Assign to this object the value of the specified 'rhs' object, and
        // return a reference providing modifiable access to this object.

    Uint128& operator|=(const Uint128& rhs);
        // Set the value of this object to the value of a the bitwise or
        // between this 128 bit integer and the specified 'rhs' value, and
        // return a reference providing mofifiable access to this object.

    Uint128& operator&=(const Uint128& rhs);
        // Set the value of this object to the value of a the bitwise and
        // between this 128 bit integer and the specified 'rhs' value, and
        // return a reference providing mofifiable access to this object.

    Uint128& operator^=(const Uint128& rhs);
        // Set the value of this object to the value of a the bitwise xor
        // between this 128 bit integer and the specified 'rhs' value, and
        // return a reference providing mofifiable access to this object.

    Uint128& operator>>=(int rhs);
        // Set the value of this object to the value of a bitwise right shift
        // of this 128 bit integer shifted by the specified 'rhs' value, and
        // return a reference providing mofifiable access to this object.  The
        // behavior is undefined unless '0 <= rhs < 128'.

    Uint128& operator<<=(int rhs);
        // Set the value of this object to the value of a bitwise left shift of
        // this 128 bit integer shifted by the specified 'rhs' value, and
        // return a reference providing mofifiable access to this object.  The
        // behavior is undefined unless '0 <= rhs < 128'.

    void setHigh(bsls::Types::Uint64 value);
        // Set the high order bits of this integer to the specified 'value'.

    void setLow(bsls::Types::Uint64 value);
        // Set the low order bits of this integer to the specified 'value'.

    // ACCESSORS
    bsls::Types::Uint64 high() const;
        // Return the high order bits of this integer.

    bsls::Types::Uint64 low() const;
        // Return the low order bits of this integer.
};

// FREE OPERATORS
bool operator==(const Uint128& lhs, const Uint128& rhs);
    // Return 'true' if the specified 'lhs' and the specified 'rhs' objects
    // have the same value, and 'false' otherwise.  Two 'Uint128' objects have
    // the same value if both of their 'low' and 'high' attributes are the
    // same.

bool operator!=(const Uint128& lhs, const Uint128& rhs);
    // Return 'true' if the specified 'lhs' and the specified 'rhs' objects do
    // not have the same value, and 'false' otherwise.  Two 'Uint128' objects
    // do not have the same value if either of their 'low' and 'high'
    // attributes are not the same.

Uint128 operator|(Uint128 lhs, const Uint128& rhs);
    // Return an Uint128 object having the value of a the bitwise or between
    // the specified 'lhs' and the specified 'rhs' value.

Uint128 operator&(Uint128 lhs, const Uint128& rhs);
    // Return an Uint128 object having the value of a the bitwise and between
    // the specified 'lhs' and the specified 'rhs' value.

Uint128 operator^(Uint128 lhs, const Uint128& rhs);
    // Return an Uint128 object having the value of a the bitwise xor between
    // the specified 'lhs' and the specified 'rhs' value.

Uint128 operator<<(Uint128 lhs, int rhs);
    // Return an 'Uint128' value equal to the value of a bitwise left shift of
    // the specified 'lhs' 128-bit integer shifted by the specified 'rhs'
    // value.  The behavior is undefined unless '0 <= rhs < 128'.

Uint128 operator>>(Uint128 lhs, int rhs);
    // Return an 'Uint128' value equal to the value of a bitwise right shift of
    // the specified 'lhs' 128-bit integer shifted by the specified 'rhs'
    // value.  The behavior is undefined unless '0 <= rhs < 128'.

Uint128 operator ~(Uint128 value);
    // Return an 'Uint128' value equal to the bitwise ones compliment of the
    // specified 'value'.

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

                        // -------------
                        // class Uint128
                        // -------------

// CREATORS
inline
Uint128::Uint128()
{
    d_low  = 0;
    d_high = 0;
}

inline
Uint128::Uint128(bsls::Types::Uint64 initialValue)
{
    d_high = 0;
    d_low  = initialValue;
}

inline
Uint128::Uint128(bsls::Types::Uint64 initialHigh,
                 bsls::Types::Uint64 initialLow)
{
    d_high = initialHigh;
    d_low  = initialLow;
}

// MANIPULATORS
inline
Uint128& Uint128::operator|=(const Uint128& rhs)
{
    d_high |= rhs.d_high;
    d_low  |= rhs.d_low;

    return *this;
}

inline
Uint128& Uint128::operator&=(const Uint128& rhs)
{
    d_high &= rhs.d_high;
    d_low  &= rhs.d_low;

    return *this;
}

inline
Uint128& Uint128::operator^=(const Uint128& rhs)
{
    d_high ^= rhs.d_high;
    d_low  ^= rhs.d_low;

    return *this;
}

inline
Uint128& Uint128::operator>>=(int rhs)
{
    if (rhs == 0) {
        return *this;                                                 // RETURN
    }

    if (rhs < 64) {
        d_low  >>= rhs;
        d_low  |= d_high << (64 - rhs);
        d_high >>= rhs;
    }
    else {
        d_low  = d_high >> (rhs - 64);
        d_high = 0;
    }

    return *this;
}

inline
Uint128& Uint128::operator<<=(int rhs)
{
    if (rhs == 0) {
        return *this;                                                 // RETURN
    }

    if (rhs < 64) {
        d_high <<= rhs;
        d_high |= d_low >> (64 - rhs);
        d_low  <<= rhs;
    }
    else {
        d_high = d_low << (rhs - 64);
        d_low  = 0;
    }

    return *this;
}

inline
void Uint128::setHigh(bsls::Types::Uint64 value)
{
    d_high = value;
}

inline
void Uint128::setLow(bsls::Types::Uint64 value)
{
    d_low = value;
}

// ACCESSORS
inline
bsls::Types::Uint64 Uint128::high() const
{
    return d_high;
}

inline
bsls::Types::Uint64 Uint128::low() const
{
    return d_low;
}


}  // close package namespace

// FREE OPERATORS
inline
bool bdldfp::operator==(const bdldfp::Uint128& lhs,
                        const bdldfp::Uint128& rhs)
{
    return lhs.high() == rhs.high() && lhs.low() == rhs.low();
}

inline
bool bdldfp::operator!=(const bdldfp::Uint128& lhs,
                        const bdldfp::Uint128& rhs)
{
    return lhs.high() != rhs.high() || lhs.low() != rhs.low();
}

inline
bdldfp::Uint128 bdldfp::operator|(      bdldfp::Uint128  lhs,
                                  const bdldfp::Uint128& rhs)
{
    return lhs |= rhs;
}

inline
bdldfp::Uint128 bdldfp::operator&(      bdldfp::Uint128  lhs,
                                  const bdldfp::Uint128& rhs)
{
    return lhs &= rhs;
}

inline
bdldfp::Uint128 bdldfp::operator^(      bdldfp::Uint128  lhs,
                                  const bdldfp::Uint128& rhs )
{
    return lhs ^= rhs;
}

inline
bdldfp::Uint128 bdldfp::operator<<(bdldfp::Uint128 lhs, int rhs)
{
    return lhs <<= rhs;
}

inline
bdldfp::Uint128 bdldfp::operator>>(bdldfp::Uint128 lhs, int rhs)
{
    return lhs >>= rhs;
}

inline
bdldfp::Uint128 bdldfp::operator~(bdldfp::Uint128 value)
{
    value.setHigh(~value.high());
    value.setLow( ~value.low());

    return value;
}

}  // close enterprise namespace
#endif

// ----------------------------------------------------------------------------
// Copyright 2014 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 ----------------------------------