public Method

Array.pack(p1)

arr.pack ( aTemplateString )  aBinaryString

Packs the contents of arr into a binary sequence according to the directives in aTemplateString (see the table below) Directives ``A,’’ ``a,’’ and ``Z’’ may be followed by a count, which gives the width of the resulting field. The remaining directives also may take a count, indicating the number of array elements to convert. If the count is an asterisk (``*’’), all remaining array elements will be converted. Any of the directives ``sSiIlL’’ may be followed by an underscore (``_’’) to use the underlying platform’s native size for the specified type; otherwise, they use a platform-independent size. Spaces are ignored in the template string. See also String#unpack.

a = [ "a", "b", "c" ]
n = [ 65, 66, 67 ]
a.pack("A3A3A3")   #=> "a  b  c  "
a.pack("a3a3a3")   #=> "a\000\000b\000\000c\000\000"
n.pack("ccc")      #=> "ABC"

Directives for pack.

Directive    Meaning
---------------------------------------------------------------
    @     |  Moves to absolute position
    A     |  ASCII string (space padded, count is width)
    a     |  ASCII string (null padded, count is width)
    B     |  Bit string (descending bit order)
    b     |  Bit string (ascending bit order)
    C     |  Unsigned char
    c     |  Char
    D, d  |  Double-precision float, native format
    E     |  Double-precision float, little-endian byte order
    e     |  Single-precision float, little-endian byte order
    F, f  |  Single-precision float, native format
    G     |  Double-precision float, network (big-endian) byte order
    g     |  Single-precision float, network (big-endian) byte order
    H     |  Hex string (high nibble first)
    h     |  Hex string (low nibble first)
    I     |  Unsigned integer
    i     |  Integer
    L     |  Unsigned long
    l     |  Long
    M     |  Quoted printable, MIME encoding (see RFC2045)
    m     |  Base64 encoded string
    N     |  Long, network (big-endian) byte order
    n     |  Short, network (big-endian) byte-order
    P     |  Pointer to a structure (fixed-length string)
    p     |  Pointer to a null-terminated string
    Q, q  |  64-bit number
    S     |  Unsigned short
    s     |  Short
    U     |  UTF-8
    u     |  UU-encoded string
    V     |  Long, little-endian byte order
    v     |  Short, little-endian byte order
    w     |  BER-compressed integer\fnm
    X     |  Back up a byte
    x     |  Null byte
    Z     |  Same as ``a'', except that null is added with *

Source Code

/*
*  call-seq:
*     arr.pack ( aTemplateString ) -> aBinaryString
*  
*  Packs the contents of <i>arr</i> into a binary sequence according to
*  the directives in <i>aTemplateString</i> (see the table below)
*  Directives ``A,'' ``a,'' and ``Z'' may be followed by a count,
*  which gives the width of the resulting field. The remaining
*  directives also may take a count, indicating the number of array
*  elements to convert. If the count is an asterisk
*  (``<code>*</code>''), all remaining array elements will be
*  converted. Any of the directives ``<code>sSiIlL</code>'' may be
*  followed by an underscore (``<code>_</code>'') to use the underlying
*  platform's native size for the specified type; otherwise, they use a
*  platform-independent size. Spaces are ignored in the template
*  string. See also <code>String#unpack</code>.
*     
*     a = [ "a", "b", "c" ]
*     n = [ 65, 66, 67 ]
*     a.pack("A3A3A3")   #=> "a  b  c  "
*     a.pack("a3a3a3")   #=> "a\000\000b\000\000c\000\000"
*     n.pack("ccc")      #=> "ABC"
*     
*  Directives for +pack+.
*
*   Directive    Meaning
*   ---------------------------------------------------------------
*       @     |  Moves to absolute position
*       A     |  ASCII string (space padded, count is width)
*       a     |  ASCII string (null padded, count is width)
*       B     |  Bit string (descending bit order)
*       b     |  Bit string (ascending bit order)
*       C     |  Unsigned char
*       c     |  Char
*       D, d  |  Double-precision float, native format
*       E     |  Double-precision float, little-endian byte order
*       e     |  Single-precision float, little-endian byte order
*       F, f  |  Single-precision float, native format
*       G     |  Double-precision float, network (big-endian) byte order
*       g     |  Single-precision float, network (big-endian) byte order
*       H     |  Hex string (high nibble first)
*       h     |  Hex string (low nibble first)
*       I     |  Unsigned integer
*       i     |  Integer
*       L     |  Unsigned long
*       l     |  Long
*       M     |  Quoted printable, MIME encoding (see RFC2045)
*       m     |  Base64 encoded string
*       N     |  Long, network (big-endian) byte order
*       n     |  Short, network (big-endian) byte-order
*       P     |  Pointer to a structure (fixed-length string)
*       p     |  Pointer to a null-terminated string
*       Q, q  |  64-bit number
*       S     |  Unsigned short
*       s     |  Short
*       U     |  UTF-8
*       u     |  UU-encoded string
*       V     |  Long, little-endian byte order
*       v     |  Short, little-endian byte order
*       w     |  BER-compressed integer\fnm
*       X     |  Back up a byte
*       x     |  Null byte
*       Z     |  Same as ``a'', except that null is added with *
*/

static VALUE
pack_pack(ary, fmt)
   VALUE ary, fmt;
{
   static char *nul10 = "\0\0\0\0\0\0\0\0\0\0";
   static char *spc10 = "          ";
   char *p, *pend;
   VALUE res, from, associates = 0;
   char type;
   long items, len, idx, plen;
   char *ptr;
#ifdef NATINT_PACK
   int natint;         /* native integer */
#endif

   StringValue(fmt);
   p = RSTRING(fmt)->ptr;
   pend = p + RSTRING(fmt)->len;
   res = rb_str_buf_new(0);

   items = RARRAY(ary)->len;
   idx = 0;

#define TOO_FEW (rb_raise(rb_eArgError, toofew), 0)
#define THISFROM (items > 0 ? RARRAY(ary)->ptr[idx] : TOO_FEW)
#define NEXTFROM (items-- > 0 ? RARRAY(ary)->ptr[idx++] : TOO_FEW)

   while (p < pend) {
       if (RSTRING(fmt)->ptr + RSTRING(fmt)->len != pend) {
           rb_raise(rb_eRuntimeError, "format string modified");
       }
       type = *p++;           /* get data type */
#ifdef NATINT_PACK
       natint = 0;
#endif

       if (ISSPACE(type)) continue;
       if (type == '#') {
           while ((p < pend) && (*p != '\n')) {
               p++;
           }
           continue;
       }
       if (*p == '_' || *p == '!') {
           const char *natstr = "sSiIlL";

           if (strchr(natstr, type)) {
#ifdef NATINT_PACK
               natint = 1;
#endif
               p++;
           }
           else {
               rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
           }
       }
       if (*p == '*') {       /* set data length */
           len = strchr("@Xxu", type) ? 0 : items;
           p++;
       }
       else if (ISDIGIT(*p)) {
           len = strtoul(p, (char**)&p, 10);
       }
       else {
           len = 1;
       }

       switch (type) {
         case 'A': case 'a': case 'Z':
         case 'B': case 'b':
         case 'H': case 'h':
           from = NEXTFROM;
           if (NIL_P(from)) {
               ptr = "";
               plen = 0;
           }
           else {
               StringValue(from);
               ptr = RSTRING(from)->ptr;
               plen = RSTRING(from)->len;
               OBJ_INFECT(res, from);
           }

           if (p[-1] == '*')
               len = plen;

           switch (type) {
             case 'a':                /* arbitrary binary string (null padded)  */
             case 'A':                /* ASCII string (space padded) */
             case 'Z':                /* null terminated ASCII string  */
               if (plen >= len) {
                   rb_str_buf_cat(res, ptr, len);
                   if (p[-1] == '*' && type == 'Z')
                       rb_str_buf_cat(res, nul10, 1);
               }
               else {
                   rb_str_buf_cat(res, ptr, plen);
                   len -= plen;
                   while (len >= 10) {
                       rb_str_buf_cat(res, (type == 'A')?spc10:nul10, 10);
                       len -= 10;
                   }
                   rb_str_buf_cat(res, (type == 'A')?spc10:nul10, len);
               }
               break;

             case 'b':                /* bit string (ascending) */
               {
                   int byte = 0;
                   long i, j = 0;

                   if (len > plen) {
                       j = (len - plen + 1)/2;
                       len = plen;
                   }
                   for (i=0; i++ < len; ptr++) {
                       if (*ptr & 1)
                           byte |= 128;
                       if (i & 7)
                           byte >>= 1;
                       else {
                           char c = byte & 0xff;
                           rb_str_buf_cat(res, &c, 1);
                           byte = 0;
                       }
                   }
                   if (len & 7) {
                       char c;
                       byte >>= 7 - (len & 7);
                       c = byte & 0xff;
                       rb_str_buf_cat(res, &c, 1);
                   }
                   len = j;
                   goto grow;
               }
               break;

             case 'B':                /* bit string (descending) */
               {
                   int byte = 0;
                   long i, j = 0;

                   if (len > plen) {
                       j = (len - plen + 1)/2;
                       len = plen;
                   }
                   for (i=0; i++ < len; ptr++) {
                       byte |= *ptr & 1;
                       if (i & 7)
                           byte <<= 1;
                       else {
                           char c = byte & 0xff;
                           rb_str_buf_cat(res, &c, 1);
                           byte = 0;
                       }
                   }
                   if (len & 7) {
                       char c;
                       byte <<= 7 - (len & 7);
                       c = byte & 0xff;
                       rb_str_buf_cat(res, &c, 1);
                   }
                   len = j;
                   goto grow;
               }
               break;

             case 'h':                /* hex string (low nibble first) */
               {
                   int byte = 0;
                   long i, j = 0;

                   if (len > plen) {
                       j = (len - plen + 1)/2;
                       len = plen;
                   }
                   for (i=0; i++ < len; ptr++) {
                       if (ISALPHA(*ptr))
                           byte |= (((*ptr & 15) + 9) & 15) << 4;
                       else
                           byte |= (*ptr & 15) << 4;
                       if (i & 1)
                           byte >>= 4;
                       else {
                           char c = byte & 0xff;
                           rb_str_buf_cat(res, &c, 1);
                           byte = 0;
                       }
                   }
                   if (len & 1) {
                       char c = byte & 0xff;
                       rb_str_buf_cat(res, &c, 1);
                   }
                   len = j;
                   goto grow;
               }
               break;

             case 'H':                /* hex string (high nibble first) */
               {
                   int byte = 0;
                   long i, j = 0;

                   if (len > plen) {
                       j = (len - plen + 1)/2;
                       len = plen;
                   }
                   for (i=0; i++ < len; ptr++) {
                       if (ISALPHA(*ptr))
                           byte |= ((*ptr & 15) + 9) & 15;
                       else
                           byte |= *ptr & 15;
                       if (i & 1)
                           byte <<= 4;
                       else {
                           char c = byte & 0xff;
                           rb_str_buf_cat(res, &c, 1);
                           byte = 0;
                       }
                   }
                   if (len & 1) {
                       char c = byte & 0xff;
                       rb_str_buf_cat(res, &c, 1);
                   }
                   len = j;
                   goto grow;
               }
               break;
           }
           break;

         case 'c':            /* signed char */
         case 'C':            /* unsigned char */
           while (len-- > 0) {
               char c;

               from = NEXTFROM;
               c = num2i32(from);
               rb_str_buf_cat(res, &c, sizeof(char));
           }
           break;

         case 's':            /* signed short */
         case 'S':            /* unsigned short */
           while (len-- > 0) {
               short s;

               from = NEXTFROM;
               s = num2i32(from);
               rb_str_buf_cat(res, OFF16(&s), NATINT_LEN(short,2));
           }
           break;

         case 'i':            /* signed int */
         case 'I':            /* unsigned int */
           while (len-- > 0) {
               long i;

               from = NEXTFROM;
               i = num2i32(from);
               rb_str_buf_cat(res, OFF32(&i), NATINT_LEN(int,4));
           }
           break;

         case 'l':            /* signed long */
         case 'L':            /* unsigned long */
           while (len-- > 0) {
               long l;

               from = NEXTFROM;
               l = num2i32(from);
               rb_str_buf_cat(res, OFF32(&l), NATINT_LEN(long,4));
           }
           break;

         case 'q':            /* signed quad (64bit) int */
         case 'Q':            /* unsigned quad (64bit) int */
           while (len-- > 0) {
               char tmp[QUAD_SIZE];

               from = NEXTFROM;
               rb_quad_pack(tmp, from);
               rb_str_buf_cat(res, (char*)&tmp, QUAD_SIZE);
           }
           break;

         case 'n':            /* unsigned short (network byte-order)  */
           while (len-- > 0) {
               unsigned short s;

               from = NEXTFROM;
               s = num2i32(from);
               s = NATINT_HTONS(s);
               rb_str_buf_cat(res, OFF16(&s), NATINT_LEN(short,2));
           }
           break;

         case 'N':            /* unsigned long (network byte-order) */
           while (len-- > 0) {
               unsigned long l;

               from = NEXTFROM;
               l = num2i32(from);
               l = NATINT_HTONL(l);
               rb_str_buf_cat(res, OFF32(&l), NATINT_LEN(long,4));
           }
           break;

         case 'v':            /* unsigned short (VAX byte-order) */
           while (len-- > 0) {
               unsigned short s;

               from = NEXTFROM;
               s = num2i32(from);
               s = NATINT_HTOVS(s);
               rb_str_buf_cat(res, OFF16(&s), NATINT_LEN(short,2));
           }
           break;

         case 'V':            /* unsigned long (VAX byte-order) */
           while (len-- > 0) {
               unsigned long l;

               from = NEXTFROM;
               l = num2i32(from);
               l = NATINT_HTOVL(l);
               rb_str_buf_cat(res, OFF32(&l), NATINT_LEN(long,4));
           }
           break;

         case 'f':            /* single precision float in native format */
         case 'F':            /* ditto */
           while (len-- > 0) {
               float f;

               from = NEXTFROM;
               f = RFLOAT(rb_Float(from))->value;
               rb_str_buf_cat(res, (char*)&f, sizeof(float));
           }
           break;

         case 'e':            /* single precision float in VAX byte-order */
           while (len-- > 0) {
               float f;
               FLOAT_CONVWITH(ftmp);

               from = NEXTFROM;
               f = RFLOAT(rb_Float(from))->value;
               f = HTOVF(f,ftmp);
               rb_str_buf_cat(res, (char*)&f, sizeof(float));
           }
           break;

         case 'E':            /* double precision float in VAX byte-order */
           while (len-- > 0) {
               double d;
               DOUBLE_CONVWITH(dtmp);

               from = NEXTFROM;
               d = RFLOAT(rb_Float(from))->value;
               d = HTOVD(d,dtmp);
               rb_str_buf_cat(res, (char*)&d, sizeof(double));
           }
           break;

         case 'd':            /* double precision float in native format */
         case 'D':            /* ditto */
           while (len-- > 0) {
               double d;

               from = NEXTFROM;
               d = RFLOAT(rb_Float(from))->value;
               rb_str_buf_cat(res, (char*)&d, sizeof(double));
           }
           break;

         case 'g':            /* single precision float in network byte-order */
           while (len-- > 0) {
               float f;
               FLOAT_CONVWITH(ftmp);

               from = NEXTFROM;
               f = RFLOAT(rb_Float(from))->value;
               f = HTONF(f,ftmp);
               rb_str_buf_cat(res, (char*)&f, sizeof(float));
           }
           break;

         case 'G':            /* double precision float in network byte-order */
           while (len-- > 0) {
               double d;
               DOUBLE_CONVWITH(dtmp);

               from = NEXTFROM;
               d = RFLOAT(rb_Float(from))->value;
               d = HTOND(d,dtmp);
               rb_str_buf_cat(res, (char*)&d, sizeof(double));
           }
           break;

         case 'x':            /* null byte */
         grow:
           while (len >= 10) {
               rb_str_buf_cat(res, nul10, 10);
               len -= 10;
           }
           rb_str_buf_cat(res, nul10, len);
           break;

         case 'X':            /* back up byte */
         shrink:
           plen = RSTRING(res)->len;
           if (plen < len)
               rb_raise(rb_eArgError, "X outside of string");
           RSTRING(res)->len = plen - len;
           RSTRING(res)->ptr[plen - len] = '\0';
           break;

         case '@':            /* null fill to absolute position */
           len -= RSTRING(res)->len;
           if (len > 0) goto grow;
           len = -len;
           if (len > 0) goto shrink;
           break;

         case '%':
           rb_raise(rb_eArgError, "%% is not supported");
           break;

         case 'U':            /* Unicode character */
           while (len-- > 0) {
               long l;
               char buf[8];
               int le;

               from = NEXTFROM;
               from = rb_to_int(from);
               l = NUM2INT(from);
               if (l < 0) {
                   rb_raise(rb_eRangeError, "pack(U): value out of range");
               }
               le = uv_to_utf8(buf, l);
               rb_str_buf_cat(res, (char*)buf, le);
           }
           break;

         case 'u':            /* uuencoded string */
         case 'm':            /* base64 encoded string */
           from = NEXTFROM;
           StringValue(from);
           ptr = RSTRING(from)->ptr;
           plen = RSTRING(from)->len;

           if (len <= 2)
               len = 45;
           else
               len = len / 3 * 3;
           while (plen > 0) {
               long todo;

               if (plen > len)
                   todo = len;
               else
                   todo = plen;
               encodes(res, ptr, todo, type);
               plen -= todo;
               ptr += todo;
           }
           break;

         case 'M':            /* quoted-printable encoded string */
           from = rb_obj_as_string(NEXTFROM);
           if (len <= 1)
               len = 72;
           qpencode(res, from, len);
           break;

         case 'P':            /* pointer to packed byte string */
           from = THISFROM;
           if (!NIL_P(from)) {
               StringValue(from);
               if (RSTRING(from)->len < len) {
                   rb_raise(rb_eArgError, "too short buffer for P(%ld for %ld)",
                            RSTRING(from)->len, len);
               }
           }
           len = 1;
           /* FALL THROUGH */
         case 'p':            /* pointer to string */
           while (len-- > 0) {
               char *t;
               from = NEXTFROM;
               if (NIL_P(from)) {
                   t = 0;
               }
               else {
                   t = StringValuePtr(from);
               }
               if (!associates) {
                   associates = rb_ary_new();
               }
               rb_ary_push(associates, from);
               rb_obj_taint(from);
               rb_str_buf_cat(res, (char*)&t, sizeof(char*));
           }
           break;

         case 'w':            /* BER compressed integer  */
           while (len-- > 0) {
               unsigned long ul;
               VALUE buf = rb_str_new(0, 0);
               char c, *bufs, *bufe;

               from = NEXTFROM;
               if (TYPE(from) == T_BIGNUM) {
                   VALUE big128 = rb_uint2big(128);
                   while (TYPE(from) == T_BIGNUM) {
                       from = rb_big_divmod(from, big128);
                       c = NUM2INT(RARRAY(from)->ptr[1]) | 0x80; /* mod */
                       rb_str_buf_cat(buf, &c, sizeof(char));
                       from = RARRAY(from)->ptr[0]; /* div */
                   }
               }

               {
                   long l = NUM2LONG(from);
                   if (l < 0) {
                       rb_raise(rb_eArgError, "can't compress negative numbers");
                   }
                   ul = l;
               }

               while (ul) {
                   c = ((ul & 0x7f) | 0x80);
                   rb_str_buf_cat(buf, &c, sizeof(char));
                   ul >>=  7;
               }

               if (RSTRING(buf)->len) {
                   bufs = RSTRING(buf)->ptr;
                   bufe = bufs + RSTRING(buf)->len - 1;
                   *bufs &= 0x7f; /* clear continue bit */
                   while (bufs < bufe) { /* reverse */
                       c = *bufs;
                       *bufs++ = *bufe;
                       *bufe-- = c;
                   }
                   rb_str_buf_cat(res, RSTRING(buf)->ptr, RSTRING(buf)->len);
               }
               else {
                   c = 0;
                   rb_str_buf_cat(res, &c, sizeof(char));
               }
           }
           break;

         default:
           break;
       }
   }

   if (associates) {
       rb_str_associate(res, associates);
   }
   return res;
}
Comments

Have your say
Please use Textile formatting (click here for a cheat sheet). Use <code/> and <pre/> for code samples.
Click here to login with OpenID to to post comments.