Method: Array#pack

Defined in:
pack.c

#packObject

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 ("_") or exclamation mark ("!") 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.

Integer      | Array   |
Directive    | Element | Meaning
---------------------------------------------------------------------------
   C         | Integer | 8-bit unsigned integer (unsigned char)
   S         | Integer | 16-bit unsigned integer, native endian (uint16_t)
   L         | Integer | 32-bit unsigned integer, native endian (uint32_t)
   Q         | Integer | 64-bit unsigned integer, native endian (uint64_t)
             |         |
   c         | Integer | 8-bit signed integer (char)
   s         | Integer | 16-bit signed integer, native endian (int16_t)
   l         | Integer | 32-bit signed integer, native endian (int32_t)
   q         | Integer | 64-bit signed integer, native endian (int64_t)
             |         |
   S_, S!    | Integer | unsigned short, native endian
   I, I_, I! | Integer | unsigned int, native endian
   L_, L!    | Integer | unsigned long, native endian
             |         |
   s_, s!    | Integer | signed short, native endian
   i, i_, i! | Integer | signed int, native endian
   l_, l!    | Integer | signed long, native endian
             |         |
   n         | Integer | 16-bit unsigned integer, network (big-endian) byte order
   N         | Integer | 32-bit unsigned integer, network (big-endian) byte order
   v         | Integer | 16-bit unsigned integer, VAX (little-endian) byte order
   V         | Integer | 32-bit unsigned integer, VAX (little-endian) byte order
             |         |
   U         | Integer | UTF-8 character
   w         | Integer | BER-compressed integer

Float        |         |
Directive    |         | Meaning
---------------------------------------------------------------------------
   D, d      | Float   | double-precision float, native format
   F, f      | Float   | single-precision float, native format
   E         | Float   | double-precision float, little-endian byte order
   e         | Float   | single-precision float, little-endian byte order
   G         | Float   | double-precision float, network (big-endian) byte order
   g         | Float   | single-precision float, network (big-endian) byte order

String       |         |
Directive    |         | Meaning
---------------------------------------------------------------------------
   A         | String  | arbitrary binary string (space padded, count is width)
   a         | String  | arbitrary binary string (null padded, count is width)
   Z         | String  | same as ``a'', except that null is added with *
   B         | String  | bit string (MSB first)
   b         | String  | bit string (LSB first)
   H         | String  | hex string (high nibble first)
   h         | String  | hex string (low nibble first)
   u         | String  | UU-encoded string
   M         | String  | quoted printable, MIME encoding (see RFC2045)
   m         | String  | base64 encoded string (see RFC 2045, count is width)
             |         | (if count is 0, no line feed are added, see RFC 4648)
   P         | String  | pointer to a structure (fixed-length string)
   p         | String  | pointer to a null-terminated string

Misc.        |         |
Directive    |         | Meaning
---------------------------------------------------------------------------
   @         | ---     | moves to absolute position
   X         | ---     | back up a byte
   x         | ---     | null byte


# File 'pack.c'

/*
 *  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>'') or
 *  exclamation mark (``<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+.
 *
 *   Integer      | Array   |
 *   Directive    | Element | Meaning
 *   ---------------------------------------------------------------------------
 *      C         | Integer | 8-bit unsigned integer (unsigned char)
 *      S         | Integer | 16-bit unsigned integer, native endian (uint16_t)
 *      L         | Integer | 32-bit unsigned integer, native endian (uint32_t)
 *      Q         | Integer | 64-bit unsigned integer, native endian (uint64_t)
 *                |         |
 *      c         | Integer | 8-bit signed integer (char)
 *      s         | Integer | 16-bit signed integer, native endian (int16_t)
 *      l         | Integer | 32-bit signed integer, native endian (int32_t)
 *      q         | Integer | 64-bit signed integer, native endian (int64_t)
 *                |         |
 *      S_, S!    | Integer | unsigned short, native endian
 *      I, I_, I! | Integer | unsigned int, native endian
 *      L_, L!    | Integer | unsigned long, native endian
 *                |         |
 *      s_, s!    | Integer | signed short, native endian
 *      i, i_, i! | Integer | signed int, native endian
 *      l_, l!    | Integer | signed long, native endian
 *                |         |
 *      n         | Integer | 16-bit unsigned integer, network (big-endian) byte order
 *      N         | Integer | 32-bit unsigned integer, network (big-endian) byte order
 *      v         | Integer | 16-bit unsigned integer, VAX (little-endian) byte order
 *      V         | Integer | 32-bit unsigned integer, VAX (little-endian) byte order
 *                |         |
 *      U         | Integer | UTF-8 character
 *      w         | Integer | BER-compressed integer
 *                
 *   Float        |         |
 *   Directive    |         | Meaning
 *   ---------------------------------------------------------------------------
 *      D, d      | Float   | double-precision float, native format
 *      F, f      | Float   | single-precision float, native format
 *      E         | Float   | double-precision float, little-endian byte order
 *      e         | Float   | single-precision float, little-endian byte order
 *      G         | Float   | double-precision float, network (big-endian) byte order
 *      g         | Float   | single-precision float, network (big-endian) byte order
 *                
 *   String       |         |
 *   Directive    |         | Meaning
 *   ---------------------------------------------------------------------------
 *      A         | String  | arbitrary binary string (space padded, count is width)
 *      a         | String  | arbitrary binary string (null padded, count is width)
 *      Z         | String  | same as ``a'', except that null is added with *
 *      B         | String  | bit string (MSB first)
 *      b         | String  | bit string (LSB first)
 *      H         | String  | hex string (high nibble first)
 *      h         | String  | hex string (low nibble first)
 *      u         | String  | UU-encoded string
 *      M         | String  | quoted printable, MIME encoding (see RFC2045)
 *      m         | String  | base64 encoded string (see RFC 2045, count is width)
 *                |         | (if count is 0, no line feed are added, see RFC 4648)
 *      P         | String  | pointer to a structure (fixed-length string)
 *      p         | String  | pointer to a null-terminated string
 *                
 *   Misc.        |         |
 *   Directive    |         | Meaning
 *   ---------------------------------------------------------------------------
 *      @         | ---     | moves to absolute position
 *      X         | ---     | back up a byte
 *      x         | ---     | null byte
 */

static VALUE
pack_pack(VALUE ary, VALUE fmt)
{
    static const char nul10[] = "\0\0\0\0\0\0\0\0\0\0";
    static const char spc10[] = "          ";
    const char *p, *pend;
    VALUE res, from, associates = 0;
    char type;
    long items, len, idx, plen;
    const char *ptr;
    int enc_info = 1;       /* 0 - BINARY, 1 - US-ASCII, 2 - UTF-8 */
#ifdef NATINT_PACK
    int natint;     /* native integer */
#endif
    int signed_p, integer_size, bigendian_p;

    StringValue(fmt);
    p = RSTRING_PTR(fmt);
    pend = p + RSTRING_LEN(fmt);
    res = rb_str_buf_new(0);

    items = RARRAY_LEN(ary);
    idx = 0;

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

    while (p < pend) {
    if (RSTRING_PTR(fmt) + RSTRING_LEN(fmt) != 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 == '!') {
        static 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
                : strchr("PMm", type) ? 1
                : items;
        p++;
    }
    else if (ISDIGIT(*p)) {
        errno = 0;
        len = STRTOUL(p, (char**)&p, 10);
        if (errno) {
        rb_raise(rb_eRangeError, "pack length too big");
        }
    }
    else {
        len = 1;
    }

    switch (type) {
      case 'U':
        /* if encoding is US-ASCII, upgrade to UTF-8 */
        if (enc_info == 1) enc_info = 2;
        break;
      case 'm': case 'M': case 'u':
        /* keep US-ASCII (do nothing) */
        break;
      default:
        /* fall back to BINARY */
        enc_info = 0;
        break;
    }
    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_PTR(from);
        plen = RSTRING_LEN(from);
        OBJ_INFECT(res, from);
        }

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

        switch (type) {
          case 'a':        /* arbitrary binary string (null padded)  */
          case 'A':         /* arbitrary binary string (ASCII space padded) */
          case 'Z':         /* null terminated 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 + 1) / 2 - (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 + 1) / 2 - (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 = (char)num2i32(from);
        rb_str_buf_cat(res, &c, sizeof(char));
        }
        break;

      case 's':        /* signed short */
            signed_p = 1;
            integer_size = NATINT_LEN(short, 2);
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

      case 'S':        /* unsigned short */
            signed_p = 0;
            integer_size = NATINT_LEN(short, 2);
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

      case 'i':        /* signed int */
            signed_p = 1;
            integer_size = (int)sizeof(int);
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

      case 'I':        /* unsigned int */
            signed_p = 0;
            integer_size = (int)sizeof(int);
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

      case 'l':        /* signed long */
            signed_p = 1;
            integer_size = NATINT_LEN(long, 4);
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

      case 'L':        /* unsigned long */
            signed_p = 0;
            integer_size = NATINT_LEN(long, 4);
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

      case 'q':        /* signed quad (64bit) int */
            signed_p = 1;
            integer_size = 8;
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

      case 'Q':        /* unsigned quad (64bit) int */
            signed_p = 0;
            integer_size = 8;
            bigendian_p = BIGENDIAN_P();
            goto pack_integer;

      case 'n':        /* unsigned short (network byte-order)  */
            signed_p = 0;
            integer_size = 2;
            bigendian_p = 1;
            goto pack_integer;

      case 'N':        /* unsigned long (network byte-order) */
            signed_p = 0;
            integer_size = 4;
            bigendian_p = 1;
            goto pack_integer;

      case 'v':        /* unsigned short (VAX byte-order) */
            signed_p = 0;
            integer_size = 2;
            bigendian_p = 0;
            goto pack_integer;

      case 'V':        /* unsigned long (VAX byte-order) */
            signed_p = 0;
            integer_size = 4;
            bigendian_p = 0;
            goto pack_integer;

          pack_integer:
            switch (integer_size) {
#if defined(HAVE_INT16_T) && !defined(FORCE_BIG_PACK)
              case SIZEOF_INT16_T:
        while (len-- > 0) {
                    union {
                        int16_t i;
                        char a[sizeof(int16_t)];
                    } v;

            from = NEXTFROM;
            v.i = (int16_t)num2i32(from);
            if (bigendian_p != BIGENDIAN_P()) v.i = swap16(v.i);
            rb_str_buf_cat(res, v.a, sizeof(int16_t));
        }
        break;
#endif

#if defined(HAVE_INT32_T) && !defined(FORCE_BIG_PACK)
              case SIZEOF_INT32_T:
        while (len-- > 0) {
            union {
                        int32_t i;
                        char a[sizeof(int32_t)];
                    } v;

            from = NEXTFROM;
            v.i = (int32_t)num2i32(from);
            if (bigendian_p != BIGENDIAN_P()) v.i = swap32(v.i);
            rb_str_buf_cat(res, v.a, sizeof(int32_t));
        }
        break;
#endif

#if defined(HAVE_INT64_T) && SIZEOF_LONG == SIZEOF_INT64_T && !defined(FORCE_BIG_PACK)
              case SIZEOF_INT64_T:
        while (len-- > 0) {
            union {
                        int64_t i;
                        char a[sizeof(int64_t)];
                    } v;

            from = NEXTFROM;
            v.i = num2i32(from); /* can return 64bit value if SIZEOF_LONG == SIZEOF_INT64_T */
            if (bigendian_p != BIGENDIAN_P()) v.i = swap64(v.i);
            rb_str_buf_cat(res, v.a, sizeof(int64_t));
        }
        break;
#endif

          default:
                if (integer_size > MAX_INTEGER_PACK_SIZE)
                    rb_bug("unexpected intger size for pack: %d", integer_size);
                while (len-- > 0) {
                    union {
                        unsigned long i[(MAX_INTEGER_PACK_SIZE+SIZEOF_LONG-1)/SIZEOF_LONG];
                        char a[(MAX_INTEGER_PACK_SIZE+SIZEOF_LONG-1)/SIZEOF_LONG*SIZEOF_LONG];
                    } v;
                    int num_longs = (integer_size+SIZEOF_LONG-1)/SIZEOF_LONG;
                    int i;

                    from = NEXTFROM;
                    rb_big_pack(from, v.i, num_longs);
                    if (bigendian_p) {
                        for (i = 0; i < num_longs/2; i++) {
                            unsigned long t = v.i[i];
                            v.i[i] = v.i[num_longs-1-i];
                            v.i[num_longs-1-i] = t;
                        }
                    }
            if (bigendian_p != BIGENDIAN_P()) {
                        for (i = 0; i < num_longs; i++)
                            v.i[i] = swapl(v.i[i]);
                    }
                    rb_str_buf_cat(res,
                                   bigendian_p ?
                                     v.a + sizeof(long)*num_longs - integer_size :
                                     v.a,
                                   integer_size);
                }
                break;
        }
        break;

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

        from = NEXTFROM;
        f = (float)RFLOAT_VALUE(rb_to_float(from));
        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 = (float)RFLOAT_VALUE(rb_to_float(from));
        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_VALUE(rb_to_float(from));
        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_VALUE(rb_to_float(from));
        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 = (float)RFLOAT_VALUE(rb_to_float(from));
        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_VALUE(rb_to_float(from));
        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_LEN(res);
        if (plen < len)
        rb_raise(rb_eArgError, "X outside of string");
        rb_str_set_len(res, plen - len);
        break;

      case '@':        /* null fill to absolute position */
        len -= RSTRING_LEN(res);
        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) {
        SIGNED_VALUE l;
        char buf[8];
        int le;

        from = NEXTFROM;
        from = rb_to_int(from);
        l = NUM2LONG(from);
        if (l < 0) {
            rb_raise(rb_eRangeError, "pack(U): value out of range");
        }
        le = rb_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_PTR(from);
        plen = RSTRING_LEN(from);

        if (len == 0 && type == 'm') {
        encodes(res, ptr, plen, type, 0);
        ptr += plen;
        break;
        }
        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, 1);
        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_LEN(from) < len) {
            rb_raise(rb_eArgError, "too short buffer for P(%ld for %ld)",
                 RSTRING_LEN(from), 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_PTR(from)[1]) | 0x80; /* mod */
            rb_str_buf_cat(buf, &c, sizeof(char));
            from = RARRAY_PTR(from)[0]; /* div */
            }
        }

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

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

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

      default:
        break;
    }
    }

    if (associates) {
    rb_str_associate(res, associates);
    }
    OBJ_INFECT(res, fmt);
    switch (enc_info) {
      case 1:
    ENCODING_CODERANGE_SET(res, rb_usascii_encindex(), ENC_CODERANGE_7BIT);
    break;
      case 2:
    rb_enc_set_index(res, rb_utf8_encindex());
    break;
      default:
    /* do nothing, keep ASCII-8BIT */
    break;
    }
    return res;
}