/*****************************************************************************
 * memendian.c
 *
 * DESCRIPTION
 *    This file contains all the utility functions that the Driver uses to
 * solve endian'ness related issues.
 *
 * HISTORY
 *   9/2002 Arthur Taylor (MDL / RSIS): Created.
 *  12/2002 Rici Yu, Fangyu Chi, Mark Armstrong, & Tim Boyer
 *          (RY,FC,MA,&TB): Code Review 2.
 *
 * NOTES
 *****************************************************************************
 */
#include <stdio.h>
#include <string.h>
#include "memendian.h"

/*****************************************************************************
 * memswp() -- Review 12/2002
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   To swap memory in the Data array based on the knownledge that there are
 * "num_elem" elements, each of size "elem_size".
 *
 * ARGUMENTS
 *      Data = A pointer to the data to be swapped. (Input/Output)
 * elem_size = The size of an individual element. (Input)
 *  num_elem = The number of elements to swap. (Input)
 *
 * FILES/DATABASES: None
 *
 * RETURNS: void
 *
 * HISTORY
 *   9/2002 Arthur Taylor (MDL/RSIS): Created.
 *  12/2002 (RY,FC,MA,&TB): Code Review.
 *
 * NOTES
 * 1) A similar routine was provided with the GRIB2 library.  It was called:
 * "unpk_swap".  Since it had the restriction that it only dealt with long
 * ints, I felt that I needed more flexibility.  In addition this procedure
 * may be more efficient.  I did an operation count for swapping an array
 * that consisted of 1 4 byte int.
 * "unpk_swap" = 46 operations, "memswp" = 33.
 * 2) Could try this with exclusive or?
 *****************************************************************************
 */
void memswp (void *Data, const size_t elem_size, const size_t num_elem)
{
   size_t j;            /* Element count */
   char *data;          /* Allows us to treat Data as an array of char. */
   char temp;           /* A temporary holder of a byte when swapping. */
   char *ptr, *ptr2;    /* Pointers to the two bytes to swap. */

   if (elem_size == 1) {
      return;
   }
   data = (char *) Data;
   for (j = 0; j < elem_size * num_elem; j += elem_size) {
      ptr = data + j;
      ptr2 = ptr + elem_size - 1;
      while (ptr2 > ptr) {
         temp = *ptr;
         *(ptr++) = *ptr2;
         *(ptr2--) = temp;
      }
   }
}

/*****************************************************************************
 * revmemcpy() -- Review 12/2002
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   To copy memory similar to memcpy, but in a reverse manner.  In order to
 * have the same arguments as memcpy, this can not handle arrays... For
 * arrays use revmemcpyRay().  Returns the same thing that memcpy does.
 *
 * ARGUMENTS
 * Dst = The destination for the data. (Output)
 * Src = The source of the data. (Input)
 * len = The length of Src in bytes. (Input)
 *
 * FILES/DATABASES: None
 *
 * RETURNS: void *
 *
 * HISTORY
 *   9/2002 Arthur Taylor (MDL/RSIS): Created.
 *  12/2002 (RY,FC,MA,&TB): Code Review.
 *
 * NOTES
 * 1) This came about as I was trying to improve on the use of memcpy.  I
 *    figured that revmemcpy would be faster than memcpy followed by memswp.
 * 2) Assumes that Dst is allocated to a size of Src.
 * 3) Problems with MEMCPY if len != sizeof (dst)... Is it left or right
 *    justified?
 *****************************************************************************
 */
void *revmemcpy (void *Dst, void *Src, const size_t len)
{
   size_t j;            /* Byte count */
   char *src = (char *) Src; /* Allows us to treat Src as an array of char. */
   char *dst = (char *) Dst; /* Allows us to treat Dst as an array of char. */

   src = src + len - 1;
   for (j = 0; j < len; ++j) {
      *(dst++) = *(src--);
   }
   return Dst;
}

/*****************************************************************************
 * revmemcpyRay() -- Review 12/2002
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   To copy memory similar to memcpy, but in a reverse manner.  This handles
 * the case when we need to reverse memcpy an array of data.
 *
 * ARGUMENTS
 *       Dst = The destination for the data. (Output)
 *       Src = The source of the data. (Input)
 * elem_size = The size of a single element. (Input)
 *  num_elem = The number of elements in Src. (Input)
 *
 * FILES/DATABASES: None
 *
 * RETURNS: void *
 *
 * HISTORY
 *   9/2002 Arthur Taylor (MDL/RSIS): Created.
 *  12/2002 (RY,FC,MA,&TB): Code Review.
 *
 * NOTES
 * 1) Assumes that Dst is allocated to a size of Src.
 *****************************************************************************
 */
void *revmemcpyRay (void *Dst, void *Src, const size_t elem_size,
                    const size_t num_elem)
{
   size_t i;            /* Element count. */
   size_t j;            /* Byte count. */
   char *src = (char *) Src; /* Allows us to treat Src as an array of char. */
   char *dst = (char *) Dst; /* Allows us to treat Dst as an array of char. */

   if (elem_size == 1) {
      return memcpy (Dst, Src, num_elem);
   }
   src -= (elem_size + 1);
   for (i = 0; i < num_elem; ++i) {
      src += 2 * elem_size;
      for (j = 0; j < elem_size; ++j) {
         *(dst++) = *(src--);
      }
   }
   return Dst;
}

/*****************************************************************************
 * memBitRead() --
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   To read bits from an uChar buffer array of memory.  Assumes BufLoc is
 * valid before first call.  Typically this means do a "bufLoc = 8;" before
 * the first call.
 *
 * ARGUMENTS
 *       Dst = Where to put the results. (Output)
 *    dstLen = Length in bytes of Dst. (Input)
 *       Src = The data to read the bits from. (Input)
 *   numBits = How many bits to read. (Input)
 *    BufLoc = Which bit to start reading from in Src.
 *             Starts at 8 goes to 1. (Input/Output)
 *   numUsed = How many bytes from Src were used while reading (Output)
 *
 * FILES/DATABASES: None
 *
 * RETURNS:
 *   Returns 1 on error, 0 if ok.
 *
 * HISTORY
 *   4/2003 Arthur Taylor (MDL/RSIS): Created
 *   5/2004 AAT: Bug in call to MEMCPY_BIG when numBytes != dstLen.
 *          On big endian machines we need to right justify the number.
 *
 * NOTES
 * 1) Assumes binary bit stream is "big endian". Resulting in no byte
 *    boundaries ie 00100110101101 => 001001 | 10101101
 *****************************************************************************
 */
char memBitRead (void *Dst, size_t dstLen, void *Src, size_t numBits,
                 uChar * bufLoc, size_t * numUsed)
{
   uChar *src = (uChar *) Src; /* Allows us to treat Src as an array of
                                * char. */
   uChar *dst = (uChar *) Dst; /* Allows us to treat Dst as an array of
                                * char. */
   size_t numBytes;     /* How many bytes are needed in dst. */
   uChar dstLoc;        /* Where we are writing to in dst. */
   uChar *ptr;          /* Current byte we are writing to in dst. */
   static uChar BitMask[] = {
      0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
   };

   if (numBits == 0) {
      memset (Dst, 0, dstLen);
      (*numUsed) = 0;
      return 0;
   }
   numBytes = ((numBits - 1) / 8) + 1;
   if (dstLen < numBytes) {
      return 1;
   }
   memset (Dst, 0, dstLen);
   dstLoc = ((numBits - 1) % 8) + 1;
   if ((*bufLoc == 8) && (dstLoc == 8)) {
#ifdef LITTLE_ENDIAN
      MEMCPY_BIG (Dst, Src, numBytes);
#else
      /* If numBytes != dstLen, then we need to right justify the ans */
      MEMCPY_BIG (dst + (dstLen - numBytes), Src, numBytes);
#endif
      (*numUsed) = numBytes;
      return 0;
   }
#ifdef LITTLE_ENDIAN
   ptr = dst + (numBytes - 1);
#else
   ptr = dst + (dstLen - numBytes);
#endif

   *numUsed = 0;
   /* Deal with most significant byte in dst. */
   if (*bufLoc >= dstLoc) {
#ifdef LITTLE_ENDIAN
      (*ptr--) |= ((*src & BitMask[*bufLoc]) >> (*bufLoc - dstLoc));
#else
      (*ptr++) |= ((*src & BitMask[*bufLoc]) >> (*bufLoc - dstLoc));
#endif
      (*bufLoc) -= dstLoc;
   } else {
      if (*bufLoc != 0) {
         *ptr |= ((*src & BitMask[*bufLoc]) << (dstLoc - *bufLoc));
         /* Assert: dstLoc should now be dstLoc - InitBufLoc */
         dstLoc = dstLoc - *bufLoc;
         /* Assert: bufLoc should now be 0 */
      }
      src++;
      (*numUsed)++;
      /* Assert: bufLoc should now be 8 */
      /* Assert: We want to >> by bufLoc - dstLoc = 8 - dstLoc */
#ifdef LITTLE_ENDIAN
      *(ptr--) |= (*src >> (8 - dstLoc));
#else
      *(ptr++) |= (*src >> (8 - dstLoc));
#endif
      (*bufLoc) = 8 - dstLoc;
   }
   /* Assert: dstLoc should now be 8, but we don't use again in procedure. */

   /* We have now reached the state which we want after each iteration of
    * the loop.  That is initDstLoc == 8, initBufLoc = bufLoc < dstLoc. */
#ifdef LITTLE_ENDIAN
   while (ptr >= dst) {
#else
   while (ptr < dst + dstLen) {
#endif
      if (*bufLoc != 0) {
         *ptr |= ((*src & BitMask[*bufLoc]) << (8 - *bufLoc));
         /* Assert: dstLoc should now be initDstLoc (8) - initBufLoc */
         /* Assert: bufLoc should now be 0 */
      }
      src++;
      (*numUsed)++;
      /* Assert: bufLoc should now be 8 */
      /* Assert: dstLoc should now be initDstLoc (8) - initBufLoc */
      /* Assert: We want to >> by bufLoc - dstLoc = (8 - (8 - initbufLoc)). */
#ifdef LITTLE_ENDIAN
      *(ptr--) |= (*src >> *bufLoc);
#else
      *(ptr++) |= (*src >> *bufLoc);
#endif
   }
   if (*bufLoc == 0) {
      (*numUsed)++;
      *bufLoc = 8;
   }
   return 0;
}

/*****************************************************************************
 * memBitWrite() --
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   To write bits from a data structure to an array of uChar.
 *   Assumes that the part of Dst we don't write to have been correctly
 * initialized.  Typically this means do a "memset (dst, 0, sizeof (dst));"
 * before the first call.
 *   Also assumes BufLoc is valid before first call.  Typically this means do
 * a "bufLoc = 8;" before the first call.
 *
 * ARGUMENTS
 *       Src = The data to read from. (Input)
 *    srcLen = Length in bytes of Src. (Input)
 *       Dst = The char buffer to write the bits to. (Output)
 *   numBits = How many bits to write. (Input)
 *    BufLoc = Which bit in Dst to start writing to.
 *             Starts at 8 goes to 1. (Input/Output)
 *   numUsed = How many bytes were written to Dst. (Output)
 *
 * FILES/DATABASES: None
 *
 * RETURNS:
 *   Returns 1 on error, 0 if ok.
 *
 * HISTORY
 *   4/2003 Arthur Taylor (MDL/RSIS): Created
 *
 * NOTES
 * 1) Assumes binary bit stream should be "big endian". Resulting in no byte
 *    boundaries ie 00100110101101 => 001001 | 1010110
 * 2) Assumes that Dst is already zero'ed out.
 *****************************************************************************
 */
char memBitWrite (void *Src, size_t srcLen, void *Dst, size_t numBits,
                  uChar * bufLoc, size_t * numUsed)
{
   uChar *src = (uChar *) Src; /* Allows us to treat Src as an array of
                                * char. */
   uChar *dst = (uChar *) Dst; /* Allows us to treat Dst as an array of
                                * char. */
   size_t numBytes;     /* How many bytes are needed from src. */
   uChar srcLoc;        /* Which bit we are reading from in src. */
   uChar *ptr;          /* Current byte we are reading from in src. */
   static uChar BitMask[] = {
      0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
   };

   if (numBits == 0) {
      return 0;
   }
   numBytes = ((numBits - 1) / 8) + 1;
   if (srcLen < numBytes) {
      return 1;
   }
   srcLoc = ((numBits - 1) % 8) + 1;

   if ((*bufLoc == 8) && (srcLoc == 8)) {
      MEMCPY_BIG (Dst, Src, numBytes);
      (*numUsed) = numBytes;
      return 0;
   }
#ifdef LITTLE_ENDIAN
   ptr = src + (numBytes - 1);
#else
   ptr = src + (srcLen - numBytes);
#endif

   *numUsed = 0;
   /* Deal with most significant byte in src. */
   if (*bufLoc >= srcLoc) {
#ifdef LITTLE_ENDIAN
      (*dst) |= ((*(ptr--) & BitMask[srcLoc]) << (*bufLoc - srcLoc));
#else
      (*dst) |= ((*(ptr++) & BitMask[srcLoc]) << (*bufLoc - srcLoc));
#endif
      (*bufLoc) -= srcLoc;
   } else {
      if (*bufLoc != 0) {
         (*dst) |= ((*ptr & BitMask[srcLoc]) >> (srcLoc - *bufLoc));
         /* Assert: srcLoc should now be srcLoc - InitBufLoc */
         srcLoc = srcLoc - *bufLoc;
         /* Assert: bufLoc should now be 0 */
      }
      dst++;
      (*dst) = 0;
      (*numUsed)++;
      /* Assert: bufLoc should now be 8 */
      /* Assert: We want to >> by bufLoc - srcLoc = 8 - srcLoc */
#ifdef LITTLE_ENDIAN
      (*dst) |= (*(ptr--) << (8 - srcLoc));
#else
      (*dst) |= (*(ptr++) << (8 - srcLoc));
#endif
      (*bufLoc) = 8 - srcLoc;
   }
   /* Assert: dstLoc should now be 8, but we don't use again in procedure. */

   /* We have now reached the state which we want after each iteration of
    * the loop.  That is initSrcLoc == 8, initBufLoc = bufLoc < srcLoc. */
#ifdef LITTLE_ENDIAN
   while (ptr >= src) {
#else
   while (ptr < src + srcLen) {
#endif
      if (*bufLoc == 0) {
         dst++;
         (*numUsed)++;
#ifdef LITTLE_ENDIAN
         (*dst) = *(ptr--);
#else
         (*dst) = *(ptr++);
#endif
      } else {
         (*dst) |= ((*ptr) >> (8 - *bufLoc));
         dst++;
         (*numUsed)++;
         (*dst) = 0;
#ifdef LITTLE_ENDIAN
         (*dst) |= (*(ptr--) << *bufLoc);
#else
         (*dst) |= (*(ptr++) << *bufLoc);
#endif
      }
   }
   if (*bufLoc == 0) {
      dst++;
      (*numUsed)++;
      (*bufLoc) = 8;
      (*dst) = 0;
   }
   return 0;
}

/*****************************************************************************
 * main() --
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   To test the memBitRead, and memBitWrite routines, to make sure that they
 * function correctly on some sample data..
 *
 * ARGUMENTS
 * argc = The number of arguments on the command line. (Input)
 * argv = The arguments on the command line. (Input)
 *
 * FILES/DATABASES: None
 *
 * RETURNS: void
 *
 * HISTORY
 *    4/2003 Arthur Taylor (MDL/RSIS): Created.
 *
 * NOTES
 *****************************************************************************
 */
#ifdef DEBUG_ENDIAN
int main (int argc, char **argv)
{
   uChar buff[5], buff2[5];
   uChar bufLoc = 8;
   uChar *ptr, *ptr2;
   int numUsed;

   buff[0] = 0x8f;
   buff[1] = 0x8f;
   buff[2] = 0x8f;
   buff[3] = 0x8f;
   buff[4] = 0x8f;

   bufLoc = 7;
   memBitRead (buff2, sizeof (buff2), buff, 39, &bufLoc, &numUsed);
   printf ("%d %d %d %d %d ", buff2[0], buff2[1], buff2[2], buff2[3],
           buff2[4]);
   printf ("-------should be----- ");
   printf ("143 143 143 143 15\n");

   memset (buff, 0, sizeof (buff));
   bufLoc = 8;
   ptr = buff;
   ptr2 = buff2;
   memBitWrite (ptr2, sizeof (buff2), ptr, 9, &bufLoc, &numUsed);
   ptr += numUsed;
   ptr2++;
   memBitWrite (ptr2, sizeof (buff2), ptr, 7, &bufLoc, &numUsed);
   ptr += numUsed;
   ptr2++;
   memBitWrite (ptr2, sizeof (buff2), ptr, 7, &bufLoc, &numUsed);
   ptr += numUsed;
   ptr2++;
   memBitWrite (ptr2, sizeof (buff2), ptr, 9, &bufLoc, &numUsed);
   ptr += numUsed;
   ptr2++;
   memBitWrite (ptr2, sizeof (buff2), ptr, 8, &bufLoc, &numUsed);
   ptr += numUsed;
   printf ("%d %d %d %d %d ", buff[0], buff[1], buff[2], buff[3], buff[4]);
   printf ("-------should be----- ");
   printf ("199 143 31 143 15\n");
   return 0;
}
#endif
