// -*- C++ -*-

//=============================================================================
/**
 *  @file   CDR_Base.h
 *
 *  CDR_Base.h,v 4.19 2004/05/14 17:07:35 ossama Exp
 *
 * ACE Common Data Representation (CDR) basic types.
 *
 * The current implementation assumes that the host has 1-byte,
 * 2-byte and 4-byte integral types, and that it has single
 * precision and double precision IEEE floats.
 * Those assumptions are pretty good these days, with Crays being
 * the only known exception.
 *
 *
 *  @author TAO version by
 *  @author Aniruddha Gokhale <gokhale@cs.wustl.edu>
 *  @author Carlos O'Ryan<coryan@cs.wustl.edu>
 *  @author ACE version by
 *  @author Jeff Parsons <parsons@cs.wustl.edu>
 *  @author Istvan Buki <istvan.buki@euronet.be>
 */
//=============================================================================


#ifndef ACE_CDR_BASE_H
#define ACE_CDR_BASE_H

#include /**/ "ace/pre.h"

#include "ace/config-all.h"

#if !defined (ACE_LACKS_PRAGMA_ONCE)
# pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */

#include "ace/Basic_Types.h"
#include "ace/Default_Constants.h"


class ACE_Message_Block;

/**
 * @class ACE_CDR
 *
 * @brief Keep constants and some routines common to both Output and
 * Input CDR streams.
 */
class ACE_Export ACE_CDR
{
public:
  // = Constants defined by the CDR protocol.
  // By defining as many of these constants as possible as enums we
  // ensure they get inlined and avoid pointless static memory
  // allocations.

  enum
  {
    // Note that some of these get reused as part of the standard
    // binary format: unsigned is the same size as its signed cousin,
    // float is LONG_SIZE, and double is LONGLONG_SIZE.

    OCTET_SIZE = 1,
    SHORT_SIZE = 2,
    LONG_SIZE = 4,
    LONGLONG_SIZE = 8,
    LONGDOUBLE_SIZE = 16,

    OCTET_ALIGN = 1,
    SHORT_ALIGN = 2,
    LONG_ALIGN = 4,
    LONGLONG_ALIGN = 8,
    /// @note the CORBA LongDouble alignment requirements do not
    /// match its size...
    LONGDOUBLE_ALIGN = 8,

    /// Maximal CDR 1.1 alignment: "quad precision" FP (i.e. "CDR::Long
    /// double", size as above).
    MAX_ALIGNMENT = 8,

    /// The default buffer size.
    /**
     * @todo We want to add options to control this
     *   default value, so this constant should be read as the default
     *   default value ;-)
     */
    DEFAULT_BUFSIZE = ACE_DEFAULT_CDR_BUFSIZE,

    /// The buffer size grows exponentially until it reaches this size;
    /// afterwards it grows linearly using the next constant
    EXP_GROWTH_MAX = ACE_DEFAULT_CDR_EXP_GROWTH_MAX,

    /// Once exponential growth is ruled out the buffer size increases
    /// in chunks of this size, note that this constants have the same
    /// value right now, but it does not need to be so.
    LINEAR_GROWTH_CHUNK = ACE_DEFAULT_CDR_LINEAR_GROWTH_CHUNK
  };

  /**
   * Do byte swapping for each basic IDL type size.  There exist only
   * routines to put byte, halfword (2 bytes), word (4 bytes),
   * doubleword (8 bytes) and quadword (16 byte); because those are
   * the IDL basic type sizes.
   */
  static void swap_2 (const char *orig, char *target);
  static void swap_4 (const char *orig, char *target);
  static void swap_8 (const char *orig, char *target);
  static void swap_16 (const char *orig, char *target);
  static void swap_2_array (const char *orig,
                            char *target,
                            size_t length);
  static void swap_4_array (const char *orig,
                            char *target,
                            size_t length);
  static void swap_8_array (const char *orig,
                            char *target,
                            size_t length);
  static void swap_16_array (const char *orig,
                             char *target,
                             size_t length);

  /// Align the message block to ACE_CDR::MAX_ALIGNMENT,
  /// set by the CORBA spec at 8 bytes.
  static void mb_align (ACE_Message_Block *mb);

  /**
   * Compute the size of the smallest buffer that can contain at least
   * <minsize> bytes.
   * To understand how a "best fit" is computed look at the
   * algorithm in the code.
   * Basically the buffers grow exponentially, up to a certain point,
   * then the buffer size grows linearly.
   * The advantage of this algorithm is that is rapidly grows to a
   * large value, but does not explode at the end.
   */
  static size_t first_size (size_t minsize);

  /// Compute not the smallest, but the second smallest buffer that
  /// will fir <minsize> bytes.
  static size_t next_size (size_t minsize);

  /**
   * Increase the capacity of mb to contain at least <minsize> bytes.
   * If <minsize> is zero the size is increased by an amount at least
   * large enough to contain any of the basic IDL types.  Return -1 on
   * failure, 0 on success.
   */
  static int grow (ACE_Message_Block *mb, size_t minsize);

  /// Copy a message block chain into a single message block,
  /// preserving the alignment of the first message block of the
  /// original stream, not the following message blocks.
  static void consolidate (ACE_Message_Block *dst,
                          const ACE_Message_Block *src);

  static size_t total_length (const ACE_Message_Block *begin,
                              const ACE_Message_Block *end);

  /**
   * @name Basic OMG IDL Types
   *
   * These types are for use in the CDRclasses.  The cleanest way to
   * avoid complaints from all compilers is to define them all.
   */
  //@{

  // Versions of GNU G++ less than version 3.0 defined a pre-C99
  // bool type that was an enumeration.  In some cases the enumeration
  // could be promoted to an int, causing a potential problem where a
  // 4 byte integer (for example) could be selected when attempting to
  // send a boolean over a CDR stream.
#if defined (__GNUC__) && __GNUC__ < 3
  typedef unsigned char Boolean;
#else
  typedef bool Boolean;
#endif  /* bool || __GNUC__ < 3 */
  typedef unsigned char Octet;
  typedef char Char;
  typedef ACE_WCHAR_T WChar;
  typedef ACE_INT16 Short;
  typedef ACE_UINT16 UShort;
  typedef ACE_INT32 Long;
  typedef ACE_UINT32 ULong;
  typedef ACE_UINT64 ULongLong;

#   if    (defined (_MSC_VER) && (_MSC_VER >= 900)) \
          || (defined (__BORLANDC__) && (__BORLANDC__ >= 0x530))
      typedef __int64 LongLong;
#   elif ACE_SIZEOF_LONG == 8 && !defined(_CRAYMPP)
      typedef long LongLong;
#   elif ACE_SIZEOF_LONG_LONG == 8 && !defined (ACE_LACKS_LONGLONG_T)
#     if defined (sun) && !defined (ACE_LACKS_U_LONGLONG_T) && !defined (__KCC)
              // sun #defines   u_longlong_t, maybe other platforms do also.
              // Use it, at least with g++, so that its -pedantic doesn't
              // complain about no ANSI C++ long long.
              typedef   longlong_t LongLong;
#     else
              // LynxOS 2.5.0   and Linux don't have u_longlong_t.
              typedef   long long LongLong;
#     endif /* sun */
#   else  /* no native 64 bit integer type */
#     define NONNATIVE_LONGLONG
      struct ACE_Export LongLong
        {
#     if defined (ACE_BIG_ENDIAN)
          ACE_CDR::Long h;
          ACE_CDR::Long l;
#     else
          ACE_CDR::Long l;
          ACE_CDR::Long h;
#     endif /* ! ACE_BIG_ENDIAN */

          /**
           * @name Overloaded Relation Operators.
           *
           * The canonical comparison operators.
           */
          //@{
          bool operator== (const LongLong &rhs) const;
          bool operator!= (const LongLong &rhs) const;
          //@}
        };
#   endif /* no native 64 bit integer type */

#   if defined (NONNATIVE_LONGLONG)
#     define ACE_CDR_LONGLONG_INITIALIZER {0,0}
#   else
#     define ACE_CDR_LONGLONG_INITIALIZER 0
#   endif /* NONNATIVE_LONGLONG */

#   if ACE_SIZEOF_FLOAT == 4
      typedef float Float;
#   else  /* ACE_SIZEOF_FLOAT != 4 */
      struct Float
      {
#       if ACE_SIZEOF_INT == 4
          // Use unsigned int to get word alignment.
          unsigned int f;
#       else  /* ACE_SIZEOF_INT != 4 */
          // Applications will probably have trouble with this.
          char f[4];
#         if defined(_UNICOS) && !defined(_CRAYMPP)
            Float (void);
            Float (const float &init);
            Float & operator= (const float &rhs);
            bool operator!= (const Float &rhs) const;
#         endif /* _UNICOS */
#       endif /* ACE_SIZEOF_INT != 4 */
      };
#   endif /* ACE_SIZEOF_FLOAT != 4 */

#   if ACE_SIZEOF_DOUBLE == 8
      typedef double Double;
#   else  /* ACE_SIZEOF_DOUBLE != 8 */
      struct Double
      {
#       if ACE_SIZEOF_LONG == 8
          // Use u long to get word alignment.
          unsigned long f;
#       else  /* ACE_SIZEOF_INT != 8 */
          // Applications will probably have trouble with this.
          char f[8];
#       endif /* ACE_SIZEOF_INT != 8 */
      };
#   endif /* ACE_SIZEOF_DOUBLE != 8 */

    // 94-9-32 Appendix A defines a 128 bit floating point "long
    // double" data type, with greatly extended precision and four
    // more bits of exponent (compared to "double").  This is an IDL
    // extension, not yet standard.

#    if   ACE_SIZEOF_LONG_DOUBLE == 16
       typedef long double      LongDouble;
#      define   ACE_CDR_LONG_DOUBLE_INITIALIZER 0
#    else
#      define NONNATIVE_LONGDOUBLE
#      define   ACE_CDR_LONG_DOUBLE_INITIALIZER {{0}}
       struct ACE_Export LongDouble
       {
         char ld[16];
         bool operator== (const LongDouble &rhs) const;
         bool operator!= (const LongDouble &rhs) const;
         // @@ also need other comparison operators.
       };
#    endif /* ACE_SIZEOF_LONG_DOUBLE != 16 */

  //@}

#if !defined (ACE_CDR_GIOP_MAJOR_VERSION)
#   define ACE_CDR_GIOP_MAJOR_VERSION 1
#endif /*ACE_CDR_GIOP_MAJOR_VERSION */

#if !defined (ACE_CDR_GIOP_MINOR_VERSION)
#   define ACE_CDR_GIOP_MINOR_VERSION 2
#endif /* ACE_CDR_GIOP_MINOR_VERSION */
};

#if defined (__ACE_INLINE__)
# include "ace/CDR_Base.inl"
#endif  /* __ACE_INLINE__ */


#include /**/ "ace/post.h"

#endif /* ACE_CDR_BASE_H */
