home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v941.tgz / icon.v941src.tar / icon.v941src / ipl / cfuncs / pack.c < prev    next >
C/C++ Source or Header  |  2000-07-29  |  7KB  |  262 lines

  1. /*
  2. ############################################################################
  3. #
  4. #    File:     pack.c
  5. #
  6. #    Subject:  Functions to pack and unpack binary data
  7. #
  8. #    Author:   Gregg M. Townsend
  9. #
  10. #    Date:     November 17, 1997
  11. #
  12. ############################################################################
  13. #
  14. #   This file is in the public domain.
  15. #
  16. ############################################################################
  17. #
  18. #  s := pack(value, flags, width)
  19. #  x := unpack(string, flags)
  20. #  Flag characters are as follows:
  21. #     l -- little-endian [default]
  22. #     b -- big-endian
  23. #     n -- host platform's native packing order
  24. #     i -- integer [default]
  25. #     u -- unsigned integer
  26. #     r -- real (host platform's native float or double format)
  27. #  The default width is 4.
  28. #  Integer values must fit in a standard Icon integer (not large integer).
  29. #  Consequently, a word-sized value cannot have the high bit set if unsigned.
  30. #  Floating values can only be converted to/from a string width matching
  31. #  sizeof(float) or sizeof(double).
  32. #  Size/type combinations that can't be handled produce errors.
  33. #  Valid combinations produce failure if the value overflows.
  34. #  Some of this code assumes a twos-complement architecture with 8-bit bytes.
  35. #
  36. ############################################################################
  37. #
  38. #  Requires:  Dynamic loading
  39. #
  40. ############################################################################
  41. */
  42.  
  43. #include "icall.h"
  44. #include <string.h>
  45.  
  46. #define F_LTL 0x100    /* little-endian */
  47. #define F_BIG 0x200    /* big-endian */
  48. #define F_REV 0x400    /* internal flag: reversal needed */
  49.  
  50. #define F_INT 1        /* integer */
  51. #define F_UNS 2        /* unsigned integer */
  52. #define F_REAL 4    /* real */
  53.  
  54. #define DEF_WIDTH 4    /* default width */
  55. #define MAX_WIDTH 256    /* maximum width */
  56.  
  57. static unsigned long testval = 1;
  58. #define LNATIVE (*(char*)&testval)    /* true if machine is little-endian */
  59.  
  60. static int flags(char *s, int n);
  61. static void *memrev(void *s1, void *s2, size_t n);
  62.  
  63. /*
  64.  * pack(value, flags, width)
  65.  */
  66. int pack(int argc, descriptor argv[])    /*: pack integer into bytes */
  67.    {
  68.    int f, i, n, x;
  69.    long v;
  70.    unsigned char *s, obuf[MAX_WIDTH];
  71.    union { float f; double d; unsigned char buf[MAX_WIDTH]; } u;
  72.  
  73.    /*
  74.     * check arguments
  75.     */
  76.    if (argc == 0)
  77.       Error(102);            /* no value given */
  78.  
  79.    if (argc > 1) {
  80.       ArgString(2);
  81.       if ((f = flags(StringAddr(argv[2]), StringLen(argv[2]))) == 0)
  82.          ArgError(2, 205);        /* illegal flag string */
  83.       }
  84.    else 
  85.       f = flags("", 0);
  86.  
  87.    if (argc > 2) {
  88.       ArgInteger(3);
  89.       n = IntegerVal(argv[3]);
  90.       if (n < 0 || n > MAX_WIDTH)
  91.          ArgError(3, 205);        /* too long to handle */
  92.       }
  93.    else
  94.       n = DEF_WIDTH;
  95.  
  96.    if (f & F_REAL) {
  97.  
  98.       /*
  99.        * pack real value
  100.        */
  101.       ArgReal(1);
  102.       if (n == sizeof(double))
  103.          u.d = RealVal(argv[1]);
  104.       else if (n == sizeof(float))
  105.          u.f = RealVal(argv[1]);
  106.       else
  107.          ArgError(3, 205);        /* illegal length for real value */
  108.  
  109.       if (f & F_REV)
  110.          RetStringN(memrev(obuf, u.buf, n), n);
  111.       else
  112.          RetStringN((char *)u.buf, n);
  113.       } 
  114.  
  115.    /*
  116.     * pack integer value
  117.     */
  118.    ArgInteger(1);
  119.    v = IntegerVal(argv[1]);        /* value */
  120.  
  121.    if (v >= 0)
  122.       x = 0;                /* sign extension byte */
  123.    else if (f & F_UNS)
  124.       Fail;                /* invalid unsigned value */
  125.    else
  126.       x = (unsigned char) -1;
  127.  
  128.    for (s = obuf, i = 0; i < sizeof(long); i++)  {
  129.       *s++ = v & 0xFF;            /* save in little-endian fashion */
  130.       v = ((unsigned long)v) >> 8;
  131.       }
  132.    while (i++ < n)
  133.       *s++ = x;                /* extend if > sizeof(long) */
  134.  
  135.    for (i = n; i < sizeof(long); i++)    /* check that all bits did fit */
  136.       if (obuf[i] != x)
  137.          Fail;                /* overflow */
  138.  
  139.    if (f & F_BIG)
  140.       RetStringN(memrev(u.buf, obuf, n), n);
  141.    else
  142.       RetStringN((char *)obuf, n);
  143.    }
  144.  
  145. /*
  146.  * unpack(string, flags)
  147.  */
  148. int unpack(int argc, descriptor argv[])    /*: unpack integer from bytes */
  149.    {
  150.    int f, i, n, x;
  151.    long v;
  152.    unsigned char *s;
  153.    union { float f; double d; unsigned char buf[MAX_WIDTH]; } u;
  154.  
  155.    /*
  156.     * check arguments
  157.     */
  158.    ArgString(1);
  159.    s = (unsigned char *)StringAddr(argv[1]);
  160.    n = StringLen(argv[1]);
  161.    if (n > MAX_WIDTH)
  162.       ArgError(1, 205);            /* too long to handle */
  163.  
  164.    if (argc > 1) {
  165.       ArgString(2);
  166.       if ((f = flags(StringAddr(argv[2]), StringLen(argv[2]))) == 0)
  167.          ArgError(2, 205);        /* illegal flag string */
  168.       }
  169.    else 
  170.       f = flags("", 0);
  171.  
  172.    if (f & F_REAL) {
  173.       /*
  174.        * unpack real value
  175.        */
  176.       if (f & F_REV)
  177.          memrev(u.buf, s, n);
  178.       else
  179.          memcpy(u.buf, s, n);
  180.  
  181.       if (n == sizeof(double))
  182.          RetReal(u.d);
  183.       else if (n == sizeof(float))
  184.          RetReal(u.f);
  185.       else
  186.          ArgError(1, 205);        /* illegal length for real value */
  187.       }
  188.  
  189.    /*
  190.     * unpack integer value 
  191.     */
  192.    if (f & F_BIG)
  193.       s = memrev(u.buf, s, n);        /* put in little-endian order */
  194.    for (v = i = 0; i < n && i < sizeof(long); i++)
  195.       v |= *s++ << (8 * i);        /* pack into a long */
  196.  
  197.    if (v >= 0)
  198.       x = 0;                /* sign extension byte */
  199.    else if (f & F_UNS)
  200.       Fail;                /* value overflows as unsigned */
  201.    else
  202.       x = (unsigned char) -1;
  203.  
  204.    for (; i < n; i++)            /* check bytes beyond sizeof(long) */
  205.       if (*s++ != x)
  206.          Fail;                /* value overflows a long */
  207.  
  208.    RetInteger(v);            /* return value */
  209.    }
  210.  
  211.  
  212. /*
  213.  * flags(addr, len) -- interpret flag string, return 0 if error
  214.  */
  215. static int flags(char *s, int n)
  216.    {
  217.    int f = 0;
  218.  
  219.    while (n--) switch(*s++) {
  220.       case 'l':  f |= F_LTL;                break;
  221.       case 'b':  f |= F_BIG;                break;
  222.       case 'n':  f |= (LNATIVE ? F_LTL : F_BIG);     break;
  223.       case 'i':  f |= F_INT;                break;
  224.       case 'u':  f |= F_UNS + F_INT;            break;
  225.       case 'r':  f |= F_REAL;                break;
  226.       default:     return 0;
  227.       }
  228.  
  229.    if (((f & F_LTL) && (f & F_BIG)) | ((f & F_INT) && (f & F_REAL)))
  230.       return 0;                /* illegal conflict */
  231.  
  232.    if (!(f & F_BIG))
  233.       f |= F_LTL;            /* default packing is little-endian */
  234.    if (!(f & F_REAL))
  235.       f |= F_INT;            /* default type is integer */
  236.  
  237.    if (f & (LNATIVE ? F_BIG : F_LTL))
  238.       f |= F_REV;            /* set flag if non-native mode */
  239.  
  240.    return f;
  241.    }
  242.  
  243.  
  244. /*
  245.  * memrev(s1, s2, n) -- copy reversal of s2 into s1, returning s1
  246.  */
  247. static void *memrev(void *s1, void *s2, size_t n)
  248.    {
  249.    unsigned char *c1 = s1;
  250.    unsigned char *c2 = (unsigned char *)s2 + n;
  251.    while (n-- > 0)
  252.       *c1++ = *--c2;
  253.    return s1;
  254.    }
  255.