/*___INFO__MARK_BEGIN__*/
/*************************************************************************
 * 
 *  The Contents of this file are made available subject to the terms of
 *  the Sun Industry Standards Source License Version 1.2
 * 
 *  Sun Microsystems Inc., March, 2001
 * 
 * 
 *  Sun Industry Standards Source License Version 1.2
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.2 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html
 * 
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 * 
 *   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 * 
 *   Copyright: 2001 by Sun Microsystems, Inc.
 * 
 *   All Rights Reserved.
 * 
 ************************************************************************/
/*___INFO__MARK_END__*/

#ifdef SOLARISAMD64
#  include <sys/stream.h>
#endif  

#include "pack.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
#include <rpc/types.h>

#if defined(INTERIX)
#include <arpa/inet.h>
#include "misc.h"
#endif

#include "basis_types.h"
#include "sgermon.h"
#include "cull_listP.h"
#include "sge_log.h"
#include "msg_cull.h"
#include "cull_state.h"

#include "uti/sge_stdlib.h"

/* do not compile in monitoring code */
#ifndef NO_SGE_COMPILE_DEBUG
#   define NO_SGE_COMPILE_DEBUG
#endif

#if 0
#   undef PACK_LAYER
#   define PACK_LAYER BASIS_LAYER
#endif


/****** cull/pack/--CULL_Packing ***************************************
*
*  NAME
*     CULL_Packing -- platform independent exchange format
*
*  FUNCTION
*     The cull packing functions provide a framework for a 
*     platform independent data representation.
*
*     Data is written into a packbuffer. Individual data words
*     are written in network byte order.
*
*     Data in the packbuffer can be compressed.
*
*  NOTES
*     Other platform independent formats, like XML, should be 
*     implemented.
*
*  SEE ALSO
*     cull/pack/-Versioncontrol
*
*     cull/pack/packbitfield()
*     cull/pack/unpackbitfield()
****************************************************************************
*/

/* compression_level is set to getenv("SGE_COMPRESSION_LEVEL") by
 * sge_setup(). must be from 0 (no compression) to 9 (best compression)
 * only buffers > compression_threshold are compressed. must be indicated
 * by a flag in the message header when sent. can be overridden at run-time
 * compression_threshold is set to getenv("SGE_COMPRESSION_THRESHOLD") by
 * sge_setup()
 */
/* long compression_level = Z_NO_COMPRESSION; */
long compression_level = 0;
long compression_threshold = 10 * 1024;

/* -------------------------------

   set chunk_size

   returns chunk_size used before

 */
int pack_set_chunk(
int sz 
) {
   int oldsz = cull_state_get_chunk_size();
   
   cull_state_set_chunk_size(sz);

   return oldsz;
}

/* -------------------------------

   get chunk_size

 */
int pack_get_chunk(void)
{
   return cull_state_get_chunk_size();
}

/****** cull/pack/init_packbuffer() *************************************************
*  NAME
*     init_packbuffer() -- initialize packing buffer
*
*  SYNOPSIS
*     int init_packbuffer(sge_pack_buffer *pb, int initial_size, 
*                         int just_count) 
*
*  FUNCTION
*     Initialize a packing buffer.
*     Allocates the necessary memory. If more memory is needed during the use
*     of the packbuffer, it will be reallocated increasing the size by 
*     chunk_size (see function pack_set_chunk).
*
*     Since version 6.0, version information is provided in the packbuffer and 
*     is included in sent messages.
*     For best possible backward interoperability with former versions, an 
*     integer with value 0 is padded before the version information as first 
*     word in the packbuffer. This triggeres error handling in former versions.
*
*  INPUTS
*     sge_pack_buffer *pb - the packbuffer to initialize
*     int initial_size    - the amount of memory to be allocated at 
*                           initialization.
*                           If a value of 0 is given as initial_size, a size 
*                           of chunk_size (global variable, see function 
*                           pack_set_chunk) will be used.
*     int just_count      - if true, no memory will be allocated and the 
*                           "just_count" property of the packbuffer will 
*                           be set.
*
*  RESULT
*     int - PACK_SUCCESS on success
*           PACK_ENOMEM  if memory allocation fails
*           PACK_FORMAT  if no valid packbuffer is passed
*
*  SEE ALSO
*     cull/pack/-Packing-typedefs
*     cull/pack/pack_set_chunk()
*  
*  NOTES
*     MT-NOTE: init_packbuffer() is MT safe (assumptions)
*******************************************************************************/
int init_packbuffer(
sge_pack_buffer *pb,
int initial_size,
int just_count 
) {
   DENTER(PACK_LAYER, "init_packbuffer");

   if (pb == NULL) {
      ERROR((SGE_EVENT, MSG_CULL_ERRORININITPACKBUFFER_S, MSG_CULL_PACK_FORMAT));
      DEXIT;
      return PACK_FORMAT;
   }   


   if (!just_count) {
      if (initial_size == 0) {
         initial_size = cull_state_get_chunk_size();
      } else {
         initial_size += 2 * INTSIZE;  /* space for version information */
      }
      
      memset(pb, 0, sizeof(sge_pack_buffer));
      pb->head_ptr = malloc(initial_size);
      if (!pb->head_ptr) {
         ERROR((SGE_EVENT, MSG_CULL_NOTENOUGHMEMORY_D, initial_size));
         DEXIT;
         return PACK_ENOMEM;
      }
      pb->cur_ptr = pb->head_ptr;
      pb->mem_size = initial_size;
      pb->mode = -1;    /* this indicates no compression/decompression */

      pb->bytes_used = 0;
      pb->just_count = 0;
      pb->version = CULL_VERSION;
      packint(pb, 0);              /* pad 0 byte -> error handing in former versions */
      packint(pb, pb->version);    /* version information is included in buffer      */
   } else {
      pb->head_ptr = pb->cur_ptr = NULL;
      pb->mem_size = 0;
      pb->mode = 0;    /* this indicates compression */
      pb->bytes_used = 0;
      pb->just_count = 1;
   }

   DEXIT;
   return PACK_SUCCESS;
}

/**************************************************************
 initialize packing buffer out of a normal character buffer
 set read/write pointer to the beginning

 NOTES
    MT-NOTE: init_packbuffer_from_buffer() is MT safe (assumptions)
 **************************************************************/
int init_packbuffer_from_buffer(
sge_pack_buffer *pb,
char *buf,
u_long32 buflen,
int compressed 
) {
   DENTER(PACK_LAYER, "init_packbuffer_from_buffer");

   if (!pb && !buf) {
      DEXIT;
      return PACK_FORMAT;
   }   

   /* don't understand compressed buffers */
   if(compressed) {
      DEXIT;
      return PACK_FORMAT;
   }   

   memset(pb, 0, sizeof(sge_pack_buffer));

   pb->head_ptr = buf;
   pb->cur_ptr = buf;
   pb->mem_size = buflen;
   pb->bytes_used = 0;
   pb->mode = compressed?1:-1;    /* must decompress or not ? */

   /* check cull version (only if buffer contains any data) */
   if(buflen > 0) {
      int ret;
      u_long32 pad, version;

      if((ret = unpackint(pb, &pad)) != PACK_SUCCESS) {
         DEXIT;
         return ret;
      } 

      if((ret = unpackint(pb, &version)) != PACK_SUCCESS) {
         DEXIT;
         return ret;
      }

      if(pad != 0 || version != CULL_VERSION) {
         ERROR((SGE_EVENT, MSG_CULL_PACK_WRONG_VERSION_XX, (unsigned int) version, CULL_VERSION));
         DEXIT;
         return PACK_VERSION;
      }

      pb->version = version;
   } else {
      pb->version = CULL_VERSION;
   }

   DEXIT;
   return PACK_SUCCESS;
}

/**************************************************************/
int flush_packbuffer(
sge_pack_buffer *pb 
) {
   return PACK_SUCCESS;
}

/**************************************************************/
/* MT-NOTE: clear_packbuffer() is MT safe */
void clear_packbuffer(
sge_pack_buffer *pb 
) {
   if (pb != NULL) {
      if (pb->head_ptr != NULL) {
         free(pb->head_ptr);
         pb->head_ptr = NULL;
      }
   }
}

/*************************************************************
 look whether pb is filled
 *************************************************************/
int pb_filled(
sge_pack_buffer *pb 
) {
   /* do we have more bytes used than the version information? */
   return (pb_used(pb) > (2 * INTSIZE));
}

/*************************************************************
 look for the number of bytes that are unused
 i.e. that are not yet consumed
 *************************************************************/
int pb_unused(
sge_pack_buffer *pb 
) {
   return (pb->mem_size - pb->bytes_used);
}

/*************************************************************
 look for the number of bytes that are used
 *************************************************************/
int pb_used(
sge_pack_buffer *pb 
) {
   return pb->bytes_used;
}

/* --------------------------------------------------------- */
/*                                                           */
/*            low level functions for packing                */
/*                                                           */
/* --------------------------------------------------------- */

/*
   return values:
   PACK_SUCCESS
   PACK_ENOMEM
   PACK_FORMAT
 */
int packint(sge_pack_buffer *pb, u_long32 i) 
{
   u_long32 J=0;

   DENTER(PACK_LAYER, "packint");

   if (!pb->just_count) {
      if (pb->bytes_used + INTSIZE > pb->mem_size) {
         DPRINTF(("realloc(%d + %d)\n", pb->mem_size, cull_state_get_chunk_size()));
         pb->mem_size += cull_state_get_chunk_size();
         pb->head_ptr = realloc(pb->head_ptr, pb->mem_size);
         if (!pb->head_ptr) {
            DEXIT;
            return PACK_ENOMEM;
         }
         pb->cur_ptr = pb->head_ptr + pb->bytes_used;
      }

      /* copy in packing buffer */
      J = htonl(i);
      memcpy(pb->cur_ptr, (((char *) &J) + INTOFF), INTSIZE);
      pb->cur_ptr += INTSIZE;
   }
   pb->bytes_used += INTSIZE;

   DEXIT;
   return PACK_SUCCESS;
}

#define DOUBLESIZE 8

/*
   return values:
   PACK_SUCCESS
   PACK_ENOMEM
   PACK_FORMAT
 */
int packdouble(sge_pack_buffer *pb, double d) {
/* CygWin does not know RPC u. XDR */
   char buf[32];
#if !(defined(WIN32))                                         /* var not needed */
   XDR xdrs;
   int doublesize;
#endif

   DENTER(PACK_LAYER, "packdouble");

   if (!pb->just_count) {
      if (pb->mode == -1 && pb->bytes_used + DOUBLESIZE > pb->mem_size) {
         DPRINTF(("realloc(%d + %d)\n", pb->mem_size, cull_state_get_chunk_size()));
         pb->mem_size += cull_state_get_chunk_size();
         pb->head_ptr = realloc(pb->head_ptr, pb->mem_size);
         if (!pb->head_ptr) {
            DEXIT;
            return PACK_ENOMEM;
         }
         pb->cur_ptr = pb->head_ptr + pb->bytes_used;
      }

      /* copy in packing buffer */
#if !(defined(WIN32) || defined(INTERIX))                      /* XDR not called */
      xdrmem_create(&xdrs, (caddr_t)buf, sizeof(buf), XDR_ENCODE);

      if (!(xdr_double(&xdrs, &d))) {
         DPRINTF(("error - XDR of double failed\n"));
         xdr_destroy(&xdrs);
         DEXIT;
         return PACK_FORMAT;
      }

      if ((doublesize = xdr_getpos(&xdrs)) != DOUBLESIZE) {
         DPRINTF(("error - size of XDRed double is %d\n", doublesize));
         xdr_destroy(&xdrs);
         DEXIT;
         return PACK_FORMAT;
      }
#endif/* WIN32 || INTERIX */
#if defined(INTERIX)
      wl_xdrmem_create(&xdrs, (caddr_t)buf, sizeof(buf), XDR_ENCODE);

      if (!(wl_xdr_double(&xdrs, &d))) {
         DPRINTF(("error - XDR of double failed\n"));
         wl_xdr_destroy(&xdrs);
         DEXIT;
         return PACK_FORMAT;
      }

      if ((doublesize = wl_xdr_getpos(&xdrs)) != DOUBLESIZE) {
         DPRINTF(("error - size of XDRed double is %d\n", doublesize));
         wl_xdr_destroy(&xdrs);
         DEXIT;
         return PACK_FORMAT;
      }
#endif

      memcpy(pb->cur_ptr, buf, DOUBLESIZE);
/* we have to increment the buffer even through WIN32 will not use it */
      pb->cur_ptr += DOUBLESIZE;

#if !(defined(WIN32) || defined(INTERIX))                      /* XDR not called */
      xdr_destroy(&xdrs);
#endif
#if defined(INTERIX)
      wl_xdr_destroy(&xdrs);
#endif
   }
   pb->bytes_used += DOUBLESIZE;

   DEXIT;
   return PACK_SUCCESS;
}

/* ---------------------------------------------------------

   return values:
   PACK_SUCCESS
   PACK_ENOMEM
   PACK_FORMAT

 */
int packstr(sge_pack_buffer *pb, const char *str) 
{
   DENTER(PACK_LAYER, "packstr");

   /* determine string length */
   if (str == NULL) {
      if (!pb->just_count) {
         /* is realloc necessary */
         if ((pb->bytes_used + 1) > pb->mem_size) {

            /* realloc */
            DPRINTF(("realloc(%d + %d)\n", pb->mem_size, cull_state_get_chunk_size()));
            pb->mem_size += cull_state_get_chunk_size();
            pb->head_ptr = realloc(pb->head_ptr, pb->mem_size);
            if (!pb->head_ptr) {
               DEXIT;
               return PACK_ENOMEM;
            }

            /* update cur_ptr */
            pb->cur_ptr = pb->head_ptr + pb->bytes_used;
         }
         pb->cur_ptr[0] = '\0';
         /* update cur_ptr & bytes_packed */
         pb->cur_ptr++;
      }
      pb->bytes_used++;
   } else {
      size_t n = strlen(str) + 1;

      if (!pb->just_count) {
         /* is realloc necessary */
         if ((pb->bytes_used + n) > pb->mem_size) {
            /* realloc */
            DPRINTF(("realloc(%d + %d)\n", pb->mem_size, cull_state_get_chunk_size()));
            while ((pb->bytes_used + n) > pb->mem_size)
               pb->mem_size += cull_state_get_chunk_size();
            pb->head_ptr = realloc(pb->head_ptr, pb->mem_size);
            if (!pb->head_ptr) {
               DEXIT;
               return PACK_ENOMEM;
            }

            /* update cur_ptr */
            pb->cur_ptr = pb->head_ptr + pb->bytes_used;
         }
         memcpy(pb->cur_ptr, str, n);
         /* update cur_ptr & bytes_packed */
         pb->cur_ptr += n;
      }
      pb->bytes_used += n;
   }

   DEXIT;
   return PACK_SUCCESS;
}


/****** cull/pack/packbitfield() ****************************************************
*  NAME
*     packbitfield() -- pack a bitfield 
*
*  SYNOPSIS
*     int packbitfield(sge_pack_buffer *pb, const bitfield *bitfield) 
*
*  FUNCTION
*     Writes the bitfield into the given packbuffer.
*     The following information will be written:
*        - the size of the bitfield in bits
*        - the bitfield itself as binary buffer
*
*  INPUTS
*     sge_pack_buffer *pb - the target packbuffer
*     const bitfield *bitfield   - the bitfield to pack
*
*  RESULT
*     int - PACK_SUCCESS on success,
*           else PACK_* error codes
*
*  SEE ALSO
*     uti/bitfield/--Bitfield
*     cull/pack/unpackbitfield()
*******************************************************************************/
int packbitfield(sge_pack_buffer *pb, const bitfield *bitfield)
{
   int ret;
   u_long32 size;
   u_long32 char_size;

   DENTER(PACK_LAYER, "packbitfield");

   size = sge_bitfield_get_size(bitfield);
   char_size = sge_bitfield_get_size_bytes(size);

   if((ret = packint(pb, size)) != PACK_SUCCESS) {
      DEXIT;
      return ret;
   }
   
   if((ret = packbuf(pb, sge_bitfield_get_buffer(bitfield),
                     char_size)) != PACK_SUCCESS) {
      DEXIT;
      return ret;
   }

   DEXIT;
   return PACK_SUCCESS;
}

/* ---------------------------------------------------------

   return values:
   PACK_SUCCESS
   PACK_ENOMEM
   PACK_FORMAT

 */
int packbuf(
sge_pack_buffer *pb,
const char *buf_ptr,
u_long32 buf_size 
) {

   DENTER(PACK_LAYER, "packbuf");

   if (!pb->just_count) {
      /* is realloc necessary */
#if !(defined(WIN32))                                        /* cast */
      if (buf_size + (u_long32) pb->bytes_used > (u_long32) pb->mem_size) {
#else
      if (buf_size + pb->bytes_used > pb->mem_size) {
#endif /* WIN32 */

         /* realloc */
         DPRINTF(("realloc(%d + %d)\n", pb->mem_size, cull_state_get_chunk_size()));
         pb->mem_size += cull_state_get_chunk_size();
         pb->head_ptr = realloc(pb->head_ptr, pb->mem_size);
         if (!(pb->head_ptr)) {
            DEXIT;
            return PACK_ENOMEM;
         }

         /* update cur_ptr */
         pb->cur_ptr = pb->head_ptr + pb->bytes_used;
      }

      /* copy in packing buffer */
      memcpy(pb->cur_ptr, buf_ptr, buf_size);
      /* update cur_ptr & bytes_packed */
      pb->cur_ptr += buf_size;
   }
   pb->bytes_used += buf_size;

   DEXIT;
   return PACK_SUCCESS;
}


/* --------------------------------------------------------- */
/*                                                           */
/*            low level functions for unpacking              */
/*                                                           */
/* --------------------------------------------------------- */

/* ---------------------------------------------------------

   return values:
   PACK_SUCCESS
   (PACK_ENOMEM)
   PACK_FORMAT

 */
int unpackint(sge_pack_buffer *pb, u_long32 *ip) 
{
   DENTER(PACK_LAYER, "unpackint");

   /* are there enough bytes ? */
   if (pb->bytes_used + INTSIZE > pb->mem_size) {
      *ip = 0;
      DEXIT;
      return PACK_FORMAT;
   }

   /* copy integer */
   memset(ip, 0, sizeof(u_long32));
   memcpy(((char *) ip) + INTOFF, pb->cur_ptr, INTSIZE);
   *ip = ntohl(*ip);

   /* update cur_ptr & bytes_unpacked */
   pb->cur_ptr += INTSIZE;
   pb->bytes_used += INTSIZE;

   DEXIT;
   return PACK_SUCCESS;
}

/* ---------------------------------------------------------

   return values:
   PACK_SUCCESS
   (PACK_ENOMEM)
   PACK_FORMAT

 */
int unpackdouble(sge_pack_buffer *pb, double *dp) 
{

#if !(defined(WIN32))                                       /* var not needed */
   XDR xdrs;
   char buf[32];
#endif

   DENTER(PACK_LAYER, "unpackdouble");


   /* are there enough bytes ? */
   if (pb->bytes_used + DOUBLESIZE > pb->mem_size) {
      *dp = 0;
      DEXIT;
      return PACK_FORMAT;
   }

   /* copy double */

   /* CygWin does not know RPC u. XDR */
#if !(defined(WIN32) || defined(INTERIX))                   /* XDR not called */
   memcpy(buf, pb->cur_ptr, DOUBLESIZE);
   xdrmem_create(&xdrs, buf, DOUBLESIZE, XDR_DECODE);
   if (!(xdr_double(&xdrs, dp))) {
      *dp = 0;
      DPRINTF(("error unpacking XDRed double\n"));
      xdr_destroy(&xdrs);
      DEXIT;
      return PACK_FORMAT;
   }
#endif /* WIN32 || INTERIX */
#if defined(INTERIX)
   memcpy(buf, pb->cur_ptr, DOUBLESIZE);
   wl_xdrmem_create(&xdrs, buf, DOUBLESIZE, XDR_DECODE);
   if (!(wl_xdr_double(&xdrs, dp))) {
      *dp = 0;
      DPRINTF(("error unpacking XDRed double\n"));
      wl_xdr_destroy(&xdrs);
      DEXIT;
      return PACK_FORMAT;
   }
#endif

   /* update cur_ptr & bytes_unpacked */
   pb->cur_ptr += DOUBLESIZE;
   pb->bytes_used += DOUBLESIZE;

#if !(defined(WIN32) || defined(INTERIX))                   /* XDR not called */
   xdr_destroy(&xdrs);
#endif /* WIN32 || INTERIX */
#if defined(INTIERX)
   wl_xdr_destroy(&xdrs);
#endif

   DEXIT;
   return PACK_SUCCESS;
}

/* ---------------------------------------------------------

   return values:
   PACK_SUCCESS
   PACK_ENOMEM
   PACK_FORMAT
 */
int unpackstr(sge_pack_buffer *pb, char **str) 
{
   u_long32 n;

   DENTER(PACK_LAYER, "unpackstr");

   if (!pb->cur_ptr[0]) {

      *str = NULL;

      /* update cur_ptr & bytes_unpacked */
      pb->cur_ptr++;
      pb->bytes_used++;

      /* are there enough bytes ? */
      if (pb->bytes_used > pb->mem_size) {
         DEXIT;
         return PACK_FORMAT;
      }

      DEXIT;
      return PACK_SUCCESS;
   }
   else {
      n = strlen(pb->cur_ptr) + 1;

      /* are there enough bytes ? */
      if (n + pb->bytes_used > pb->mem_size) {
         DEXIT;
         return PACK_FORMAT;
      }
      *str = strdup(pb->cur_ptr);
      if (!*str) {
         DEXIT;
         return PACK_ENOMEM;
      }
      /* update cur_ptr & bytes_unpacked */
      pb->bytes_used += n;
      pb->cur_ptr += n;
   }

   DEXIT;
   return PACK_SUCCESS;
}

/* ---------------------------------------------------------

   return values:
   PACK_SUCCESS
   PACK_ENOMEM
   PACK_FORMAT
 */
int unpackbuf(
sge_pack_buffer *pb,
char **buf_ptr,
int buf_size 
) {

   DENTER(PACK_LAYER, "unpackbuf");

   if (buf_size == 0) {
      DEXIT;
      return PACK_SUCCESS;
   }

   /* are there enough bytes ? */
   if (pb->mode == -1 && (buf_size + pb->bytes_used) > pb->mem_size) {
      DEXIT;
      return PACK_FORMAT;
   }

   /* copy buffer */
   *buf_ptr = malloc(buf_size);
   if (!*buf_ptr) {
      DEXIT;
      return PACK_ENOMEM;
   }
   memcpy(*buf_ptr, pb->cur_ptr, buf_size);
   /* update cur_ptr & bytes_unpacked */
   pb->cur_ptr += buf_size;
   pb->bytes_used += buf_size;

   DEXIT;
   return PACK_SUCCESS;
}

/****** cull/pack/unpackbitfield() ****************************************************
*  NAME
*     unpackbitfield() -- unpack a bitfield 
*
*  SYNOPSIS
*     int unpackbitfield(sge_pack_buffer *pb, bitfield *bitfield) 
*
*  FUNCTION
*     Unpacks a bitfield from a packbuffer.
*
*     If the size of the descriptor doesn't match the size of the unpacked
*     bitfield, create a new bitfield.
*
*  INPUTS
*     sge_pack_buffer *pb - the source packbuffer
*     bitfield *bitfield  - used to return the unpacked bitfield
*     int descr_size      - size of the corresponding descriptor
*
*  RESULT
*     int - PACK_SUCCESS on success,
*           else PACK_* error codes
*
*  SEE ALSO
*     uti/bitfield/--Bitfield
*     cull/pack/packbitfield()
*******************************************************************************/
int unpackbitfield(sge_pack_buffer *pb, bitfield *bitfield, int descr_size)
{
   int ret;
   u_long32 size, char_size;
   char *buffer;

   DENTER(PACK_LAYER, "unpackbitfield");

   /* create new bitfield */
   if (!sge_bitfield_init(bitfield, descr_size)) {
      DEXIT;
      return PACK_ENOMEM;
   }

   /* unpack the size in bits */
   if((ret = unpackint(pb, &size)) != PACK_SUCCESS) {
      DEXIT;
      return ret;
   }

   /* unpack contents of the bitfield */
   char_size = sge_bitfield_get_size_bytes(size);
   if((ret = unpackbuf(pb, &buffer, char_size)) != PACK_SUCCESS) {
      sge_bitfield_free_data(bitfield);
      DEXIT;
      return ret;
   }
   
   /* if bitfield matches descr, copy bitfield */
   if (size == descr_size) {
      memcpy(sge_bitfield_get_buffer(bitfield), buffer, char_size);
   }

   /* free unpacked bitfield buffer */
   FREE(buffer);

   DEXIT;
   return PACK_SUCCESS;
}

const char *cull_pack_strerror(int errnum)
{
   switch(errnum) {
      case PACK_SUCCESS:
         return MSG_CULL_PACK_SUCCESS;
      case PACK_ENOMEM:
         return MSG_CULL_PACK_ENOMEM;
      case PACK_FORMAT:
         return MSG_CULL_PACK_FORMAT;
      case PACK_BADARG:
         return MSG_CULL_PACK_BADARG;
      case PACK_VERSION:
         return MSG_CULL_PACK_VERSION;
      default:
         /* JG: TODO: we should have some global error message for all strerror functions */
         return "";
   }
}

