Pack.java

  1. /***** BEGIN LICENSE BLOCK *****
  2.  * Version: EPL 1.0/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Eclipse Public
  5.  * License Version 1.0 (the "License"); you may not use this file
  6.  * except in compliance with the License. You may obtain a copy of
  7.  * the License at http://www.eclipse.org/legal/epl-v10.html
  8.  *
  9.  * Software distributed under the License is distributed on an "AS
  10.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11.  * implied. See the License for the specific language governing
  12.  * rights and limitations under the License.
  13.  *
  14.  * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  15.  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  16.  * Copyright (C) 2003-2004 Thomas E Enebo <enebo@acm.org>
  17.  * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
  18.  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  19.  * Copyright (C) 2005 Derek Berner <derek.berner@state.nm.us>
  20.  * Copyright (C) 2006 Evan Buswell <ebuswell@gmail.com>
  21.  * Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
  22.  * Copyright (C) 2009 Joseph LaFata <joe@quibb.org>
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either of the GNU General Public License Version 2 or later (the "GPL"),
  26.  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the EPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the EPL, the GPL or the LGPL.
  35.  ***** END LICENSE BLOCK *****/
  36. package org.jruby.util;

  37. import java.math.BigInteger;
  38. import java.nio.ByteBuffer;
  39. import java.nio.ByteOrder;
  40. import org.jcodings.Encoding;

  41. import org.jcodings.specific.ASCIIEncoding;
  42. import org.jcodings.specific.USASCIIEncoding;
  43. import org.jcodings.specific.UTF8Encoding;
  44. import org.jruby.platform.Platform;
  45. import org.jruby.Ruby;
  46. import org.jruby.RubyArray;
  47. import org.jruby.RubyBignum;
  48. import org.jruby.RubyFixnum;
  49. import org.jruby.RubyFloat;
  50. import org.jruby.RubyNumeric;
  51. import org.jruby.RubyObject;
  52. import org.jruby.RubyString;
  53. import org.jruby.runtime.ThreadContext;
  54. import org.jruby.runtime.builtin.IRubyObject;

  55. public class Pack {
  56.     private static final byte[] sSp10 = "          ".getBytes();
  57.     private static final byte[] sNil10 = "\000\000\000\000\000\000\000\000\000\000".getBytes();
  58.     private static final int IS_STAR = -1;
  59.     private static final ASCIIEncoding ASCII = ASCIIEncoding.INSTANCE;
  60.     private static final USASCIIEncoding USASCII = USASCIIEncoding.INSTANCE;
  61.     private static final UTF8Encoding UTF8 = UTF8Encoding.INSTANCE;
  62.     /** Native pack type.
  63.      **/
  64.     private static final String NATIVE_CODES = "sSiIlL";
  65.     private static final String MAPPED_CODES = "sSiIqQ";
  66.    
  67.     private static final char BE = '>' - 1; // 61, only 1 char "free" b/w q and s
  68.     private static final char LE = '<'; // 60
  69.     private static final String ENDIANESS_CODES = new String(new char[] {
  70.             's' + BE, 'S' + BE/*n*/, 'i' + BE, 'I' + BE, 'l' + BE, 'L' + BE/*N*/, 'q' + BE, 'Q' + BE,
  71.             's' + LE, 'S' + LE/*v*/, 'i' + LE, 'I' + LE, 'l' + LE, 'L' + LE/*V*/, 'q' + LE, 'Q' + LE});
  72.     private static final String UNPACK_IGNORE_NULL_CODES = "cC";
  73.     private static final String PACK_IGNORE_NULL_CODES = "cCiIlLnNqQsSvV";
  74.     private static final String PACK_IGNORE_NULL_CODES_WITH_MODIFIERS = "lLsS";
  75.     private static final String sTooFew = "too few arguments";
  76.     private static final byte[] hex_table;
  77.     private static final byte[] uu_table;
  78.     private static final byte[] b64_table;
  79.     private static final byte[] sHexDigits;
  80.     private static final int[] b64_xtable = new int[256];
  81.     private static final Converter[] converters = new Converter[256];

  82.     private static long num2quad(IRubyObject arg) {
  83.         if (arg == arg.getRuntime().getNil()) {
  84.             return 0L;
  85.         }
  86.         else if (arg instanceof RubyBignum) {
  87.             BigInteger big = ((RubyBignum)arg).getValue();
  88.             return big.longValue();
  89.         }
  90.         return RubyNumeric.num2long(arg);
  91.     }

  92.     private static float obj2flt(Ruby runtime, IRubyObject o) {
  93.         return (float) TypeConverter.toFloat(runtime, o).getDoubleValue();        
  94.     }

  95.     private static double obj2dbl(Ruby runtime, IRubyObject o) {
  96.         return TypeConverter.toFloat(runtime, o).getDoubleValue();        
  97.     }    

  98.     static {
  99.         hex_table = ByteList.plain("0123456789ABCDEF");
  100.         uu_table =
  101.             ByteList.plain("`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_");
  102.         b64_table =
  103.             ByteList.plain("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
  104.         sHexDigits = ByteList.plain("0123456789abcdef0123456789ABCDEFx");

  105.         // b64_xtable for decoding Base 64
  106.         for (int i = 0; i < 256; i++) {
  107.             b64_xtable[i] = -1;
  108.         }
  109.         for (int i = 0; i < 64; i++) {
  110.             b64_xtable[(int)b64_table[i]] = i;
  111.         }

  112.         // single precision, little-endian
  113.         converters['e'] = new Converter(4) {
  114.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  115.                 return RubyFloat.newFloat(runtime, decodeFloatLittleEndian(enc));
  116.             }

  117.             @Override
  118.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  119.                 encodeFloatLittleEndian(result, obj2flt(runtime, o));
  120.             }
  121.         };
  122.         // single precision, big-endian
  123.         converters['g'] = new Converter(4) {
  124.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  125.                 return RubyFloat.newFloat(runtime, decodeFloatBigEndian(enc));
  126.             }

  127.             @Override
  128.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  129.                 encodeFloatBigEndian(result, obj2flt(runtime, o));
  130.             }
  131.         };
  132.         // single precision, native
  133.         Converter tmp = new Converter(4) {
  134.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  135.                 return RubyFloat.newFloat(runtime,
  136.                         Platform.BYTE_ORDER == Platform.BIG_ENDIAN ?
  137.                         decodeFloatBigEndian(enc) : decodeFloatLittleEndian(enc));
  138.             }

  139.             @Override
  140.             public void encode(Ruby runtime, IRubyObject o, ByteList result) {
  141.                 if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {                
  142.                     encodeFloatBigEndian(result, obj2flt(runtime, o));
  143.                 } else {
  144.                     encodeFloatLittleEndian(result, obj2flt(runtime, o));
  145.                 }
  146.             }
  147.         };
  148.         converters['F'] = tmp; // single precision, native
  149.         converters['f'] = tmp; // single precision, native

  150.         // double precision, little-endian
  151.         converters['E'] = new Converter(8) {
  152.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  153.                 return RubyFloat.newFloat(runtime, decodeDoubleLittleEndian(enc));
  154.             }

  155.             @Override
  156.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  157.                 encodeDoubleLittleEndian(result, obj2dbl(runtime, o));
  158.             }              
  159.         };
  160.         // double precision, big-endian
  161.         converters['G'] = new Converter(8) {
  162.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  163.                 return RubyFloat.newFloat(runtime, decodeDoubleBigEndian(enc));
  164.             }

  165.             @Override
  166.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  167.                 encodeDoubleBigEndian(result, obj2dbl(runtime, o));
  168.             }
  169.         };
  170.         // double precision, native
  171.         tmp = new Converter(8) {
  172.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  173.                 if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
  174.                     return RubyFloat.newFloat(runtime, decodeDoubleBigEndian(enc));
  175.                 } else {
  176.                     return RubyFloat.newFloat(runtime, decodeDoubleLittleEndian(enc));
  177.                 }
  178.             }

  179.             @Override
  180.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  181.                 encodeDoubleLittleEndian(result, obj2dbl(runtime, o));
  182.             }    
  183.         };
  184.         converters['D'] = tmp; // double precision, native
  185.         converters['d'] = tmp; // double precision, native

  186.         // signed short, little-endian
  187.         tmp = new QuadConverter(2, "Integer") {
  188.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  189.                 return runtime.newFixnum(decodeShortUnsignedLittleEndian(enc));
  190.             }

  191.             @Override
  192.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  193.                 encodeShortLittleEndian(result, overflowQuad(num2quad(o)));
  194.             }            
  195.         };
  196.         converters['v'] = tmp;
  197.         converters['S' + LE] = tmp;
  198.         // signed short, big-endian
  199.         tmp = new QuadConverter(2, "Integer") {
  200.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  201.                 return runtime.newFixnum(decodeShortUnsignedBigEndian(enc));
  202.             }

  203.             @Override
  204.             public void encode(Ruby runtime, IRubyObject o, ByteList result) {
  205.                 encodeShortBigEndian(result, overflowQuad(num2quad(o)));
  206.             }
  207.         };
  208.         converters['n'] = tmp;
  209.         converters['S' + BE] = tmp;
  210.         // signed short, native
  211.         converters['s'] = new QuadConverter(2, "Integer") {
  212.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  213.                 return runtime.newFixnum(Platform.BYTE_ORDER == Platform.BIG_ENDIAN ?
  214.                         decodeShortBigEndian(enc) : decodeShortLittleEndian(enc));
  215.             }

  216.             @Override
  217.             public void encode(Ruby runtime, IRubyObject o, ByteList result) {
  218.                 encodeShortByByteOrder(result, overflowQuad(num2quad(o))); // XXX: 0xffff0000 on BE?
  219.             }
  220.         };
  221.         // unsigned short, native
  222.         converters['S'] = new QuadConverter(2, "Integer") {
  223.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  224.                 return runtime.newFixnum(Platform.BYTE_ORDER == Platform.BIG_ENDIAN ?
  225.                     decodeShortUnsignedBigEndian(enc) : decodeShortUnsignedLittleEndian(enc));
  226.             }

  227.             @Override
  228.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  229.                 encodeShortByByteOrder(result, overflowQuad(num2quad(o)));
  230.             }
  231.         };
  232.         // signed short, little endian
  233.         converters['s' + LE] = new QuadConverter(2, "Integer") {
  234.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  235.                 return runtime.newFixnum(decodeShortLittleEndian(enc));
  236.             }

  237.             @Override
  238.             public void encode(Ruby runtime, IRubyObject o, ByteList result) {
  239.                 encodeShortLittleEndian(result, overflowQuad(num2quad(o))); // XXX: 0xffff0000 on BE?
  240.             }
  241.         };
  242.         // signed short, big endian
  243.         converters['s' + BE] = new QuadConverter(2, "Integer") {
  244.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  245.                 return runtime.newFixnum(decodeShortBigEndian(enc));
  246.             }

  247.             @Override
  248.             public void encode(Ruby runtime, IRubyObject o, ByteList result) {
  249.                 encodeShortBigEndian(result, overflowQuad(num2quad(o))); // XXX: 0xffff0000 on BE?
  250.             }
  251.         };

  252.         // signed char
  253.         converters['c'] = new Converter(1, "Integer") {
  254.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  255.                 int c = enc.get();
  256.                 return runtime.newFixnum(c > (char) 127 ? c-256 : c);
  257.             }

  258.             public void encode(Ruby runtime, IRubyObject o, ByteList result) {
  259.                 byte c = (byte) (num2quad(o) & 0xff);
  260.                 result.append(c);
  261.             }
  262.         };
  263.         // unsigned char
  264.         converters['C'] = new Converter(1, "Integer") {
  265.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  266.                 return runtime.newFixnum(enc.get() & 0xFF);
  267.             }

  268.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  269.                 byte c = o == runtime.getNil() ? 0 : (byte) (num2quad(o) & 0xff);
  270.                 result.append(c);
  271.             }
  272.         };

  273.         // unsigned long, little-endian
  274.         tmp = new Converter(4, "Integer") {
  275.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  276.                 return runtime.newFixnum(decodeIntUnsignedLittleEndian(enc));
  277.             }
  278.            
  279.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  280.                 encodeIntLittleEndian(result, (int) RubyNumeric.num2long(o));
  281.             }
  282.         };
  283.         converters['V'] = tmp;
  284.         converters['L' + LE] = tmp;
  285.         converters['I' + LE] = tmp;
  286.        
  287.         // unsigned long, big-endian
  288.         tmp = new Converter(4, "Integer") {
  289.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  290.                 return runtime.newFixnum(decodeIntUnsignedBigEndian(enc));
  291.             }
  292.            
  293.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  294.                 encodeIntBigEndian(result, (int) RubyNumeric.num2long(o));
  295.             }
  296.         };
  297.         converters['N'] = tmp;
  298.         converters['L' + BE] = tmp;
  299.         converters['I' + BE] = tmp;

  300.         // unsigned int, native
  301.         tmp = new Converter(4, "Integer") {
  302.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  303.                 if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
  304.                     return runtime.newFixnum(decodeIntUnsignedBigEndian(enc));
  305.                 } else {
  306.                     return runtime.newFixnum(decodeIntUnsignedLittleEndian(enc));
  307.                 }
  308.             }
  309.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  310.                 int s = o == runtime.getNil() ? 0 : (int) RubyNumeric.num2long(o);
  311.                 packInt_i(result, s);
  312.             }
  313.         };
  314.         converters['I'] = tmp; // unsigned int, native
  315.         converters['L'] = tmp; // unsigned long, native

  316.         // int, native
  317.         tmp = new Converter(4, "Integer") {
  318.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  319.                 int value = unpackInt_i(enc);
  320.                 return runtime.newFixnum(value);
  321.             }
  322.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  323.                 int s = o == runtime.getNil() ? 0 : (int)RubyNumeric.num2long(o);
  324.                 packInt_i(result, s);
  325.             }
  326.         };
  327.         converters['i'] = tmp; // int, native
  328.         converters['l'] = tmp; // long, native
  329.        
  330.         // int, little endian
  331.         tmp = new Converter(4, "Integer") {
  332.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  333.                 return runtime.newFixnum(decodeIntLittleEndian(enc));
  334.             }
  335.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  336.                 int s = o == runtime.getNil() ? 0 : (int)RubyNumeric.num2long(o);
  337.                 encodeIntLittleEndian(result, s);
  338.             }
  339.         };
  340.         converters['i' + LE] = tmp; // int, native
  341.         converters['l' + LE] = tmp; // long, native
  342.        
  343.         // int, big endian
  344.         tmp = new Converter(4, "Integer") {
  345.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  346.                 return runtime.newFixnum(decodeIntBigEndian(enc));
  347.             }
  348.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  349.                 int s = o == runtime.getNil() ? 0 : (int)RubyNumeric.num2long(o);
  350.                 encodeIntBigEndian(result, s);
  351.             }
  352.         };
  353.         converters['i' + BE] = tmp; // int, native
  354.         converters['l' + BE] = tmp; // long, native

  355.         // 64-bit number, native (as bignum)
  356.         converters['Q'] = new QuadConverter(8, "Integer") {
  357.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  358.                 long l = Platform.BYTE_ORDER == Platform.BIG_ENDIAN ? decodeLongBigEndian(enc) : decodeLongLittleEndian(enc);

  359.                 return RubyBignum.bignorm(runtime,BigInteger.valueOf(l).and(new BigInteger("FFFFFFFFFFFFFFFF", 16)));
  360.             }

  361.             @Override
  362.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  363.                 encodeLongByByteOrder(result, num2quad(o));
  364.             }
  365.         };
  366.         // 64-bit number, little endian (as bignum)
  367.         converters['Q' + LE] = new QuadConverter(8, "Integer") {
  368.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  369.                 long l = decodeLongLittleEndian(enc);
  370.                 return RubyBignum.bignorm(runtime,BigInteger.valueOf(l).and(new BigInteger("FFFFFFFFFFFFFFFF", 16)));
  371.             }

  372.             @Override
  373.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  374.                 encodeLongLittleEndian(result, num2quad(o));
  375.             }
  376.         };
  377.         // 64-bit number, big endian (as bignum)
  378.         converters['Q' + BE] = new QuadConverter(8, "Integer") {
  379.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  380.                 long l = decodeLongBigEndian(enc);
  381.                 return RubyBignum.bignorm(runtime,BigInteger.valueOf(l).and(new BigInteger("FFFFFFFFFFFFFFFF", 16)));
  382.             }

  383.             @Override
  384.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  385.                 encodeLongBigEndian(result, num2quad(o));
  386.             }
  387.         };
  388.         // 64-bit number, native (as fixnum)
  389.         converters['q'] = new QuadConverter(8, "Integer") {
  390.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  391.                 return runtime.newFixnum(Platform.BYTE_ORDER == Platform.BIG_ENDIAN ?
  392.                         decodeLongBigEndian(enc) : decodeLongLittleEndian(enc));
  393.             }

  394.             @Override
  395.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  396.                 encodeLongByByteOrder(result, num2quad(o));
  397.             }
  398.         };
  399.         // 64-bit number, little-endian (as fixnum)
  400.         converters['q' + LE] = new QuadConverter(8, "Integer") {
  401.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  402.                 return runtime.newFixnum(decodeLongLittleEndian(enc));
  403.             }

  404.             @Override
  405.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  406.                 encodeLongLittleEndian(result, num2quad(o));
  407.             }
  408.         };
  409.         // 64-bit number, big-endian (as fixnum)
  410.         converters['q' + BE] = new QuadConverter(8, "Integer") {
  411.             public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
  412.                 return runtime.newFixnum(decodeLongBigEndian(enc));
  413.             }

  414.             @Override
  415.             public void encode(Ruby runtime, IRubyObject o, ByteList result){
  416.                 encodeLongBigEndian(result, num2quad(o));
  417.             }
  418.         };
  419.     }

  420.     public static int unpackInt_i(ByteBuffer enc) {
  421.         int value;
  422.         if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
  423.             value = decodeIntBigEndian(enc);
  424.         } else {
  425.             value = decodeIntLittleEndian(enc);
  426.         }
  427.         return value;
  428.     }

  429.     public static ByteList packInt_i(ByteList result, int s) {
  430.         if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
  431.             encodeIntBigEndian(result, s);
  432.         } else {
  433.             encodeIntLittleEndian(result, s);
  434.         }
  435.         return result;
  436.     }

  437.     /**
  438.      * encodes a String in base64 or its uuencode variant.
  439.      * appends the result of the encoding in a StringBuffer
  440.      * @param io2Append The StringBuffer which should receive the result
  441.      * @param i2Encode The String to encode
  442.      * @param iLength The max number of characters to encode
  443.      * @param iType the type of encoding required (this is the same type as used by the pack method)
  444.      * @param tailLf true if the traililng "\n" is needed
  445.      * @return the io2Append buffer
  446.      **/
  447.     private static ByteList encodes(Ruby runtime, ByteList io2Append,byte[]charsToEncode, int startIndex, int length, int charCount, byte encodingType, boolean tailLf) {
  448.         charCount = charCount < length ? charCount : length;

  449.         io2Append.ensure(charCount * 4 / 3 + 6);
  450.         int i = startIndex;
  451.         byte[] lTranslationTable = encodingType == 'u' ? uu_table : b64_table;
  452.         byte lPadding;
  453.         if (encodingType == 'u') {
  454.             if (charCount >= lTranslationTable.length) {
  455.                 throw runtime.newArgumentError(
  456.                     ""
  457.                         + charCount
  458.                         + " is not a correct value for the number of bytes per line in a u directive.  Correct values range from 0 to "
  459.                         + lTranslationTable.length);
  460.             }
  461.             io2Append.append(lTranslationTable[charCount]);
  462.             lPadding = '`';
  463.         } else {
  464.             lPadding = '=';
  465.         }
  466.         while (charCount >= 3) {
  467.             byte lCurChar = charsToEncode[i++];
  468.             byte lNextChar = charsToEncode[i++];
  469.             byte lNextNextChar = charsToEncode[i++];
  470.             io2Append.append(lTranslationTable[077 & (lCurChar >>> 2)]);
  471.             io2Append.append(lTranslationTable[077 & (((lCurChar << 4) & 060) | ((lNextChar >>> 4) & 017))]);
  472.             io2Append.append(lTranslationTable[077 & (((lNextChar << 2) & 074) | ((lNextNextChar >>> 6) & 03))]);
  473.             io2Append.append(lTranslationTable[077 & lNextNextChar]);
  474.             charCount -= 3;
  475.         }
  476.         if (charCount == 2) {
  477.             byte lCurChar = charsToEncode[i++];
  478.             byte lNextChar = charsToEncode[i++];
  479.             io2Append.append(lTranslationTable[077 & (lCurChar >>> 2)]);
  480.             io2Append.append(lTranslationTable[077 & (((lCurChar << 4) & 060) | ((lNextChar >> 4) & 017))]);
  481.             io2Append.append(lTranslationTable[077 & (((lNextChar << 2) & 074) | (('\0' >> 6) & 03))]);
  482.             io2Append.append(lPadding);
  483.         } else if (charCount == 1) {
  484.             byte lCurChar = charsToEncode[i++];
  485.             io2Append.append(lTranslationTable[077 & (lCurChar >>> 2)]);
  486.             io2Append.append(lTranslationTable[077 & (((lCurChar << 4) & 060) | (('\0' >>> 4) & 017))]);
  487.             io2Append.append(lPadding);
  488.             io2Append.append(lPadding);
  489.         }
  490.         if (tailLf) {
  491.             io2Append.append('\n');
  492.         }
  493.         return io2Append;
  494.     }

  495.     /**
  496.      * encodes a String with the Quoted printable, MIME encoding (see RFC2045).
  497.      * appends the result of the encoding in a StringBuffer
  498.      * @param io2Append The StringBuffer which should receive the result
  499.      * @param i2Encode The String to encode
  500.      * @param iLength The max number of characters to encode
  501.      * @return the io2Append buffer
  502.      **/
  503.     private static ByteList qpencode(ByteList io2Append, ByteList i2Encode, int iLength) {
  504.         io2Append.ensure(1024);
  505.         int lCurLineLength = 0;
  506.         int lPrevChar = -1;
  507.         byte[] l2Encode = i2Encode.getUnsafeBytes();
  508.         try {
  509.             int end = i2Encode.getBegin() + i2Encode.getRealSize();
  510.             for (int i = i2Encode.getBegin(); i < end; i++) {
  511.                 int lCurChar = l2Encode[i] & 0xff;
  512.                 if (lCurChar > 126 || (lCurChar < 32 && lCurChar != '\n' && lCurChar != '\t') || lCurChar == '=') {
  513.                     io2Append.append('=');
  514.                     io2Append.append(hex_table[lCurChar >>> 4]);
  515.                     io2Append.append(hex_table[lCurChar & 0x0f]);
  516.                     lCurLineLength += 3;
  517.                     lPrevChar = -1;
  518.                 } else if (lCurChar == '\n') {
  519.                     if (lPrevChar == ' ' || lPrevChar == '\t') {
  520.                         io2Append.append('=');
  521.                         io2Append.append(lCurChar);
  522.                     }
  523.                     io2Append.append(lCurChar);
  524.                     lCurLineLength = 0;
  525.                     lPrevChar = lCurChar;
  526.                 } else {
  527.                     io2Append.append(lCurChar);
  528.                     lCurLineLength++;
  529.                     lPrevChar = lCurChar;
  530.                 }
  531.                 if (lCurLineLength > iLength) {
  532.                     io2Append.append('=');
  533.                     io2Append.append('\n');
  534.                     lCurLineLength = 0;
  535.                     lPrevChar = '\n';
  536.                 }
  537.             }
  538.         } catch (ArrayIndexOutOfBoundsException e) {
  539.             //normal exit, this should be faster than a test at each iterations for string with more than
  540.             //about 40 char
  541.         }

  542.         if (lCurLineLength > 0) {
  543.             io2Append.append('=');
  544.             io2Append.append('\n');
  545.         }
  546.         return io2Append;
  547.     }

  548.     /**
  549.      *    Decodes <i>str</i> (which may contain binary data) according to the format
  550.      *       string, returning an array of each value extracted.
  551.      *       The format string consists of a sequence of single-character directives.<br/>
  552.      *       Each directive may be followed by a number, indicating the number of times to repeat with this directive.  An asterisk (``<code>*</code>'') will use up all
  553.      *       remaining elements.  <br/>
  554.      *       The directives <code>sSiIlL</code> may each be followed by an underscore (``<code>_</code>'') to use the underlying platform's native size for the specified type; otherwise, it uses a platform-independent consistent size.  <br/>
  555.      *       Spaces are ignored in the format string.
  556.      *
  557.      *       <table border="2" width="500" bgcolor="#ffe0e0">
  558.      *           <tr>
  559.      *             <td>
  560.      * <P></P>
  561.      *         <b>Directives for <a href="ref_c_string.html#String.unpack">
  562.      *                   <code>String#unpack</code>
  563.      *                 </a>
  564.      *               </b>        <table class="codebox" cellspacing="0" border="0" cellpadding="3">
  565.      * <tr bgcolor="#ff9999">
  566.      *   <td valign="top">
  567.      *                     <b>Format</b>
  568.      *                   </td>
  569.      *   <td valign="top">
  570.      *                     <b>Function</b>
  571.      *                   </td>
  572.      *   <td valign="top">
  573.      *                     <b>Returns</b>
  574.      *                   </td>
  575.      * </tr>
  576.      * <tr>
  577.      *   <td valign="top">A</td>
  578.      *   <td valign="top">String with trailing nulls and spaces removed.</td>
  579.      *   <td valign="top">String</td>
  580.      * </tr>
  581.      * <tr>
  582.      *   <td valign="top">a</td>
  583.      *   <td valign="top">String.</td>
  584.      *   <td valign="top">String</td>
  585.      * </tr>
  586.      * <tr>
  587.      *   <td valign="top">B</td>
  588.      *   <td valign="top">Extract bits from each character (msb first).</td>
  589.      *   <td valign="top">String</td>
  590.      * </tr>
  591.      * <tr>
  592.      *   <td valign="top">b</td>
  593.      *   <td valign="top">Extract bits from each character (lsb first).</td>
  594.      *   <td valign="top">String</td>
  595.      * </tr>
  596.      * <tr>
  597.      *   <td valign="top">C</td>
  598.      *   <td valign="top">Extract a character as an unsigned integer.</td>
  599.      *   <td valign="top">Fixnum</td>
  600.      * </tr>
  601.      * <tr>
  602.      *   <td valign="top">c</td>
  603.      *   <td valign="top">Extract a character as an integer.</td>
  604.      *   <td valign="top">Fixnum</td>
  605.      * </tr>
  606.      * <tr>
  607.      *   <td valign="top">d</td>
  608.      *   <td valign="top">Treat <em>sizeof(double)</em> characters as a native
  609.      *           double.</td>
  610.      *   <td valign="top">Float</td>
  611.      * </tr>
  612.      * <tr>
  613.      *   <td valign="top">E</td>
  614.      *   <td valign="top">Treat <em>sizeof(double)</em> characters as a double in
  615.      *           little-endian byte order.</td>
  616.      *   <td valign="top">Float</td>
  617.      * </tr>
  618.      * <tr>
  619.      *   <td valign="top">e</td>
  620.      *   <td valign="top">Treat <em>sizeof(float)</em> characters as a float in
  621.      *           little-endian byte order.</td>
  622.      *   <td valign="top">Float</td>
  623.      * </tr>
  624.      * <tr>
  625.      *   <td valign="top">f</td>
  626.      *   <td valign="top">Treat <em>sizeof(float)</em> characters as a native float.</td>
  627.      *   <td valign="top">Float</td>
  628.      * </tr>
  629.      * <tr>
  630.      *   <td valign="top">G</td>
  631.      *   <td valign="top">Treat <em>sizeof(double)</em> characters as a double in
  632.      *           network byte order.</td>
  633.      *   <td valign="top">Float</td>
  634.      * </tr>
  635.      * <tr>
  636.      *   <td valign="top">g</td>
  637.      *   <td valign="top">Treat <em>sizeof(float)</em> characters as a float in
  638.      *           network byte order.</td>
  639.      *   <td valign="top">Float</td>
  640.      * </tr>
  641.      * <tr>
  642.      *   <td valign="top">H</td>
  643.      *   <td valign="top">Extract hex nibbles from each character (most
  644.      *           significant first).</td>
  645.      *   <td valign="top">String</td>
  646.      * </tr>
  647.      * <tr>
  648.      *   <td valign="top">h</td>
  649.      *   <td valign="top">Extract hex nibbles from each character (least
  650.      *           significant first).</td>
  651.      *   <td valign="top">String</td>
  652.      * </tr>
  653.      * <tr>
  654.      *   <td valign="top">I</td>
  655.      *   <td valign="top">Treat <em>sizeof(int)</em>
  656.      *                     <sup>1</sup> successive
  657.      *           characters as an unsigned native integer.</td>
  658.      *   <td valign="top">Integer</td>
  659.      * </tr>
  660.      * <tr>
  661.      *   <td valign="top">i</td>
  662.      *   <td valign="top">Treat <em>sizeof(int)</em>
  663.      *                     <sup>1</sup> successive
  664.      *           characters as a signed native integer.</td>
  665.      *   <td valign="top">Integer</td>
  666.      * </tr>
  667.      * <tr>
  668.      *   <td valign="top">L</td>
  669.      *   <td valign="top">Treat four<sup>1</sup> successive
  670.      *           characters as an unsigned native
  671.      *           long integer.</td>
  672.      *   <td valign="top">Integer</td>
  673.      * </tr>
  674.      * <tr>
  675.      *   <td valign="top">l</td>
  676.      *   <td valign="top">Treat four<sup>1</sup> successive
  677.      *           characters as a signed native
  678.      *           long integer.</td>
  679.      *   <td valign="top">Integer</td>
  680.      * </tr>
  681.      * <tr>
  682.      *   <td valign="top">M</td>
  683.      *   <td valign="top">Extract a quoted-printable string.</td>
  684.      *   <td valign="top">String</td>
  685.      * </tr>
  686.      * <tr>
  687.      *   <td valign="top">m</td>
  688.      *   <td valign="top">Extract a base64 encoded string.</td>
  689.      *   <td valign="top">String</td>
  690.      * </tr>
  691.      * <tr>
  692.      *   <td valign="top">N</td>
  693.      *   <td valign="top">Treat four characters as an unsigned long in network
  694.      *           byte order.</td>
  695.      *   <td valign="top">Fixnum</td>
  696.      * </tr>
  697.      * <tr>
  698.      *   <td valign="top">n</td>
  699.      *   <td valign="top">Treat two characters as an unsigned short in network
  700.      *           byte order.</td>
  701.      *   <td valign="top">Fixnum</td>
  702.      * </tr>
  703.      * <tr>
  704.      *   <td valign="top">P</td>
  705.      *   <td valign="top">Treat <em>sizeof(char *)</em> characters as a pointer, and
  706.      *           return <em>len</em> characters from the referenced location.</td>
  707.      *   <td valign="top">String</td>
  708.      * </tr>
  709.      * <tr>
  710.      *   <td valign="top">p</td>
  711.      *   <td valign="top">Treat <em>sizeof(char *)</em> characters as a pointer to a
  712.      *           null-terminated string.</td>
  713.      *   <td valign="top">String</td>
  714.      * </tr>
  715.      * <tr>
  716.      *   <td valign="top">S</td>
  717.      *   <td valign="top">Treat two<sup>1</sup> successive characters as an unsigned
  718.      *           short in
  719.      *           native byte order.</td>
  720.      *   <td valign="top">Fixnum</td>
  721.      * </tr>
  722.      * <tr>
  723.      *   <td valign="top">s</td>
  724.      *   <td valign="top">Treat two<sup>1</sup> successive
  725.      *           characters as a signed short in
  726.      *           native byte order.</td>
  727.      *   <td valign="top">Fixnum</td>
  728.      * </tr>
  729.      * <tr>
  730.      *   <td valign="top">U</td>
  731.      *   <td valign="top">Extract UTF-8 characters as unsigned integers.</td>
  732.      *   <td valign="top">Integer</td>
  733.      * </tr>
  734.      * <tr>
  735.      *   <td valign="top">u</td>
  736.      *   <td valign="top">Extract a UU-encoded string.</td>
  737.      *   <td valign="top">String</td>
  738.      * </tr>
  739.      * <tr>
  740.      *   <td valign="top">V</td>
  741.      *   <td valign="top">Treat four characters as an unsigned long in little-endian
  742.      *           byte order.</td>
  743.      *   <td valign="top">Fixnum</td>
  744.      * </tr>
  745.      * <tr>
  746.      *   <td valign="top">v</td>
  747.      *   <td valign="top">Treat two characters as an unsigned short in little-endian
  748.      *           byte order.</td>
  749.      *   <td valign="top">Fixnum</td>
  750.      * </tr>
  751.      * <tr>
  752.      *   <td valign="top">X</td>
  753.      *   <td valign="top">Skip backward one character.</td>
  754.      *   <td valign="top">---</td>
  755.      * </tr>
  756.      * <tr>
  757.      *   <td valign="top">x</td>
  758.      *   <td valign="top">Skip forward one character.</td>
  759.      *   <td valign="top">---</td>
  760.      * </tr>
  761.      * <tr>
  762.      *   <td valign="top">Z</td>
  763.      *   <td valign="top">String with trailing nulls removed.</td>
  764.      *   <td valign="top">String</td>
  765.      * </tr>
  766.      * <tr>
  767.      *   <td valign="top">@</td>
  768.      *   <td valign="top">Skip to the offset given by the length argument.</td>
  769.      *   <td valign="top">---</td>
  770.      * </tr>
  771.      * <tr>
  772.      *                   <td colspan="9" bgcolor="#ff9999" height="2"><img src="dot.gif" width="1" height="1"></td>
  773.      *                 </tr>
  774.      *               </table>
  775.      * <P></P>
  776.      *         <sup>1</sup>&nbsp;May be modified by appending ``_'' to the directive.
  777.      * <P></P>
  778.      *       </td>
  779.      *           </tr>
  780.      *         </table>
  781.      *
  782.      * @see RubyArray#pack
  783.      **/
  784.     public static RubyArray unpack(Ruby runtime, ByteList encodedString, ByteList formatString) {
  785.         Encoding encoding = encodedString.getEncoding();
  786.         RubyArray result = runtime.newArray();
  787.         // FIXME: potentially could just use ByteList here?
  788.         ByteBuffer format = ByteBuffer.wrap(formatString.getUnsafeBytes(), formatString.begin(), formatString.length());
  789.         ByteBuffer encode = ByteBuffer.wrap(encodedString.getUnsafeBytes(), encodedString.begin(), encodedString.length());
  790.         int type = 0;
  791.         int next = safeGet(format);

  792.         mainLoop: while (next != 0) {
  793.             type = next;
  794.             next = safeGet(format);
  795.             if (UNPACK_IGNORE_NULL_CODES.indexOf(type) != -1 && next == 0) {
  796.                 next = safeGetIgnoreNull(format);
  797.             }
  798.            
  799.             if (type == '#') {
  800.                 while (type != '\n') {
  801.                     if (next == 0) break mainLoop;
  802.                     type = next;
  803.                     next = safeGet(format);
  804.                 }
  805.             }

  806.             // Next indicates to decode using native encoding format
  807.             if (next == '_' || next == '!') {
  808.                 int index = NATIVE_CODES.indexOf(type);
  809.                 if (index == -1) {
  810.                     throw runtime.newArgumentError("'" + next +
  811.                             "' allowed only after types " + NATIVE_CODES);
  812.                 }
  813.                 type = MAPPED_CODES.charAt(index);
  814.                
  815.                 next = safeGet(format);
  816.             }
  817.            
  818.             if (next == '>' || next == '<') {
  819.                 next = next == '>' ? BE : LE;
  820.                 int index = ENDIANESS_CODES.indexOf(type + next);
  821.                 if (index == -1) {
  822.                     throw runtime.newArgumentError("'" + (char)next +
  823.                             "' allowed only after types sSiIlLqQ");
  824.                 }
  825.                 type = ENDIANESS_CODES.charAt(index);
  826.                 next = safeGet(format);
  827.                
  828.                 if (next == '_' || next == '!') next = safeGet(format);
  829.             }

  830.             // How many occurrences of 'type' we want
  831.             int occurrences = 0;
  832.             if (next == 0) {
  833.                 occurrences = 1;
  834.             } else {
  835.                 if (next == '*') {
  836.                     occurrences = IS_STAR;
  837.                     next = safeGet(format);
  838.                 } else if (ASCII.isDigit(next)) {
  839.                     occurrences = 0;
  840.                     do {
  841.                         occurrences = occurrences * 10 + Character.digit((char)(next & 0xFF), 10);
  842.                         next = safeGet(format);
  843.                     } while (next != 0 && ASCII.isDigit(next));
  844.                 } else {
  845.                     occurrences = type == '@' ? 0 : 1;
  846.                 }
  847.             }

  848.             // See if we have a converter for the job...
  849.             Converter converter = converters[type];
  850.             if (converter != null) {
  851.                 decode(runtime, encode, occurrences, result, converter);
  852.                 type = next;
  853.                 continue;
  854.             }

  855.             // Otherwise the unpack should be here...
  856.             switch (type) {
  857.                 case '@' :
  858.                     try {
  859.                         if (occurrences == IS_STAR) {
  860.                             encode.position(encodedString.begin() + encode.remaining());
  861.                         } else {
  862.                             encode.position(encodedString.begin() + occurrences);
  863.                         }
  864.                     } catch (IllegalArgumentException iae) {
  865.                         throw runtime.newArgumentError("@ outside of string");
  866.                     }
  867.                     break;
  868.                 case '%' :
  869.                     throw runtime.newArgumentError("% is not supported");
  870.                 case 'A' :
  871.                     {
  872.                     if (occurrences == IS_STAR || occurrences > encode.remaining()) {
  873.                         occurrences = encode.remaining();
  874.                     }

  875.                     byte[] potential = new byte[occurrences];
  876.                     encode.get(potential);

  877.                     for (int t = occurrences - 1; occurrences > 0; occurrences--, t--) {
  878.                         byte c = potential[t];

  879.                            if (c != '\0' && c != ' ') {
  880.                                break;
  881.                            }
  882.                     }

  883.                     result.append(RubyString.newString(runtime, new ByteList(potential, 0, occurrences, encoding, false)));
  884.                     }
  885.                     break;
  886.                 case 'Z' :
  887.                     {
  888.                         boolean isStar = (occurrences == IS_STAR);

  889.                         if (occurrences == IS_STAR || occurrences > encode.remaining()) {
  890.                             occurrences = encode.remaining();
  891.                         }

  892.                         byte[] potential = new byte[occurrences];
  893.                         int t = 0;

  894.                         while (t < occurrences) {
  895.                             byte b = encode.get();
  896.                             if (b == 0) {
  897.                                 break;
  898.                             }
  899.                             potential[t] = b;
  900.                             t++;
  901.                         }

  902.                         result.append(RubyString.newString(runtime, new ByteList(potential, 0, t, encoding, false)));

  903.                         // In case when the number of occurences is
  904.                         // explicitly specified, we have to read up
  905.                         // the remaining garbage after the '\0' to
  906.                         // satisfy the requested pattern.
  907.                         if (!isStar) {
  908.                             if (t < occurrences) {
  909.                                 // We encountered '\0' when
  910.                                 // were reading the buffer above,
  911.                                 // increment the number of read bytes.
  912.                                 t++;
  913.                             }

  914.                             while (t < occurrences) {
  915.                                 encode.get();
  916.                                 t++;
  917.                             }
  918.                         }
  919.                     }
  920.                     break;
  921.                 case 'a' :
  922.                     if (occurrences == IS_STAR || occurrences > encode.remaining()) {
  923.                         occurrences = encode.remaining();
  924.                     }
  925.                     byte[] potential = new byte[occurrences];
  926.                     encode.get(potential);
  927.                     result.append(RubyString.newString(runtime, new ByteList(potential, encoding, false)));
  928.                     break;
  929.                 case 'b' :
  930.                     {
  931.                         if (occurrences == IS_STAR || occurrences > encode.remaining() * 8) {
  932.                             occurrences = encode.remaining() * 8;
  933.                         }
  934.                         int bits = 0;
  935.                         byte[] lElem = new byte[occurrences];
  936.                         for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) {
  937.                             if ((lCurByte & 7) != 0) {
  938.                                 bits >>>= 1;
  939.                             } else {
  940.                                 bits = encode.get();
  941.                             }
  942.                             lElem[lCurByte] = (bits & 1) != 0 ? (byte)'1' : (byte)'0';
  943.                         }
  944.                         result.append(RubyString.newString(runtime, new ByteList(lElem, encoding, false)));
  945.                     }
  946.                     break;
  947.                 case 'B' :
  948.                     {
  949.                         if (occurrences == IS_STAR || occurrences > encode.remaining() * 8) {
  950.                             occurrences = encode.remaining() * 8;
  951.                         }
  952.                         int bits = 0;
  953.                         byte[] lElem = new byte[occurrences];
  954.                         for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) {
  955.                             if ((lCurByte & 7) != 0) {
  956.                                 bits <<= 1;
  957.                             } else {
  958.                                 bits = encode.get();
  959.                             }
  960.                             lElem[lCurByte] = (bits & 128) != 0 ? (byte)'1' : (byte)'0';
  961.                         }

  962.                         result.append(RubyString.newString(runtime, new ByteList(lElem, encoding, false)));
  963.                     }
  964.                     break;
  965.                 case 'h' :
  966.                     {
  967.                         if (occurrences == IS_STAR || occurrences > encode.remaining() * 2) {
  968.                             occurrences = encode.remaining() * 2;
  969.                         }
  970.                         int bits = 0;
  971.                         byte[] lElem = new byte[occurrences];
  972.                         for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) {
  973.                             if ((lCurByte & 1) != 0) {
  974.                                 bits >>>= 4;
  975.                             } else {
  976.                                 bits = encode.get();
  977.                             }
  978.                             lElem[lCurByte] = sHexDigits[bits & 15];
  979.                         }
  980.                         result.append(RubyString.newString(runtime, new ByteList(lElem, encoding, false)));
  981.                     }
  982.                     break;
  983.                 case 'H' :
  984.                     {
  985.                         if (occurrences == IS_STAR || occurrences > encode.remaining() * 2) {
  986.                             occurrences = encode.remaining() * 2;
  987.                         }
  988.                         int bits = 0;
  989.                         byte[] lElem = new byte[occurrences];
  990.                         for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) {
  991.                             if ((lCurByte & 1) != 0) {
  992.                                 bits <<= 4;
  993.                             } else {
  994.                                 bits = encode.get();
  995.                             }
  996.                             lElem[lCurByte] = sHexDigits[(bits >>> 4) & 15];
  997.                         }
  998.                         result.append(RubyString.newString(runtime, new ByteList(lElem, encoding, false)));
  999.                     }
  1000.                     break;

  1001.                 case 'u':
  1002.                 {
  1003.                     int length = encode.remaining() * 3 / 4;
  1004.                     byte[] lElem = new byte[length];
  1005.                     int index = 0;
  1006.                     int s = 0;
  1007.                     int total = 0;
  1008.                     if (length > 0) s = encode.get();
  1009.                     while (encode.hasRemaining() && s > ' ' && s < 'a') {
  1010.                         int a, b, c, d;
  1011.                         byte[] hunk = new byte[3];

  1012.                         int len = (s - ' ') & 077;
  1013.                         s = safeGet(encode);
  1014.                         total += len;
  1015.                         if (total > length) {
  1016.                             len -= total - length;
  1017.                             total = length;
  1018.                         }

  1019.                         while (len > 0) {
  1020.                             int mlen = len > 3 ? 3 : len;

  1021.                             if (encode.hasRemaining() && s >= ' ') {
  1022.                                 a = (s - ' ') & 077;
  1023.                                 s = safeGet(encode);
  1024.                             } else
  1025.                                 a = 0;
  1026.                             if (encode.hasRemaining() && s >= ' ') {
  1027.                                 b = (s - ' ') & 077;
  1028.                                 s = safeGet(encode);
  1029.                             } else
  1030.                                 b = 0;
  1031.                             if (encode.hasRemaining() && s >= ' ') {
  1032.                                 c = (s - ' ') & 077;
  1033.                                 s = safeGet(encode);
  1034.                             } else
  1035.                                 c = 0;
  1036.                             if (encode.hasRemaining() && s >= ' ') {
  1037.                                 d = (s - ' ') & 077;
  1038.                                 s = safeGet(encode);
  1039.                             } else
  1040.                                 d = 0;
  1041.                             hunk[0] = (byte)((a << 2 | b >> 4) & 255);
  1042.                             hunk[1] = (byte)((b << 4 | c >> 2) & 255);
  1043.                             hunk[2] = (byte)((c << 6 | d) & 255);

  1044.                             for (int i = 0; i < mlen; i++) lElem[index++] = hunk[i];
  1045.                             len -= mlen;
  1046.                         }
  1047.                         if (s == '\r') {
  1048.                             s = safeGet(encode);
  1049.                         }
  1050.                         if (s == '\n') {
  1051.                             s = safeGet(encode);
  1052.                         }
  1053.                         else if (encode.hasRemaining()) {
  1054.                             if (safeGet(encode) == '\n') {
  1055.                                 safeGet(encode); // Possible Checksum Byte
  1056.                             } else if (encode.hasRemaining()) {
  1057.                                 encode.position(encode.position() - 1);
  1058.                             }
  1059.                         }
  1060.                     }
  1061.                     result.append(RubyString.newString(runtime, new ByteList(lElem, 0, index, encoding, false)));
  1062.                 }
  1063.                 break;

  1064.                 case 'm':
  1065.                 {
  1066.                     int length = encode.remaining()*3/4;
  1067.                     byte[] lElem = new byte[length];
  1068.                     int a = -1, b = -1, c = 0, d;
  1069.                     int index = 0;
  1070.                     int s = -1;

  1071.                     while (encode.hasRemaining()) {
  1072.                         a = b = c = d = -1;
  1073.                        
  1074.                         // obtain a
  1075.                         s = safeGet(encode);
  1076.                         while (((a = b64_xtable[s]) == -1) && encode.hasRemaining()) {
  1077.                             s = safeGet(encode);
  1078.                         }
  1079.                         if (a == -1) break;
  1080.                        
  1081.                         // obtain b
  1082.                         s = safeGet(encode);
  1083.                         while (((b = b64_xtable[s]) == -1) && encode.hasRemaining()) {
  1084.                             s = safeGet(encode);
  1085.                         }
  1086.                         if (b == -1) break;
  1087.                        
  1088.                         // obtain c
  1089.                         s = safeGet(encode);
  1090.                         while (((c = b64_xtable[s]) == -1) && encode.hasRemaining()) {
  1091.                             if (s == '=') break;
  1092.                             s = safeGet(encode);
  1093.                         }
  1094.                         if ((s == '=') || c == -1) {
  1095.                             if (s == '=') {
  1096.                                 encode.position(encode.position() - 1);
  1097.                             }
  1098.                             break;
  1099.                         }
  1100.                        
  1101.                         // obtain d
  1102.                         s = safeGet(encode);
  1103.                         while (((d = b64_xtable[s]) == -1) && encode.hasRemaining()) {
  1104.                             if (s == '=') break;
  1105.                             s = safeGet(encode);
  1106.                         }
  1107.                         if ((s == '=') || d == -1) {
  1108.                             if (s == '=') {
  1109.                                 encode.position(encode.position() - 1);
  1110.                             }
  1111.                             break;
  1112.                         }

  1113.                         // calculate based on a, b, c and d
  1114.                         lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
  1115.                         lElem[index++] = (byte)((b << 4 | c >> 2) & 255);
  1116.                         lElem[index++] = (byte)((c << 6 | d) & 255);
  1117.                     }

  1118.                     if (a != -1 && b != -1) {
  1119.                         if (c == -1 && s == '=') {
  1120.                             lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
  1121.                         } else if(c != -1 && s == '=') {
  1122.                             lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
  1123.                             lElem[index++] = (byte)((b << 4 | c >> 2) & 255);
  1124.                         }
  1125.                     }
  1126.                     result.append(RubyString.newString(runtime, new ByteList(lElem, 0, index,
  1127.                             ASCIIEncoding.INSTANCE, false)));
  1128.                 }
  1129.                 break;

  1130.                 case 'M' :
  1131.                     {
  1132.                         byte[] lElem = new byte[Math.max(encode.remaining(),0)];
  1133.                         int index = 0;
  1134.                         for(;;) {
  1135.                             if (!encode.hasRemaining()) break;
  1136.                             int c = safeGet(encode);
  1137.                             if (c != '=') {
  1138.                                 lElem[index++] = (byte)c;
  1139.                             } else {
  1140.                                 if (!encode.hasRemaining()) break;
  1141.                                 encode.mark();
  1142.                                 int c1 = safeGet(encode);
  1143.                                 if (c1 == '\n' || (c1 == '\r' && (c1 = safeGet(encode)) == '\n')) continue;
  1144.                                 int d1 = Character.digit(c1, 16);
  1145.                                 if (d1 == -1) {
  1146.                                     encode.reset();
  1147.                                     break;
  1148.                                 }
  1149.                                 encode.mark();
  1150.                                 if (!encode.hasRemaining()) break;
  1151.                                 int c2 = safeGet(encode);
  1152.                                 int d2 = Character.digit(c2, 16);
  1153.                                 if (d2 == -1) {
  1154.                                     encode.reset();
  1155.                                     break;
  1156.                                 }
  1157.                                 byte value = (byte)(d1 << 4 | d2);
  1158.                                 lElem[index++] = value;
  1159.                             }
  1160.                         }
  1161.                         result.append(RubyString.newString(runtime, new ByteList(lElem, 0, index,
  1162.                                 ASCIIEncoding.INSTANCE, false)));
  1163.                     }
  1164.                     break;
  1165.                 case 'U' :
  1166.                     {
  1167.                         if (occurrences == IS_STAR || occurrences > encode.remaining()) {
  1168.                             occurrences = encode.remaining();
  1169.                         }

  1170.                         while (occurrences-- > 0 && encode.remaining() > 0) {
  1171.                             try {
  1172.                                 // TODO: for now, we use a faithful
  1173.                                 // reimplementation of MRI's algorithm,
  1174.                                 // but should use UTF8Encoding facilities
  1175.                                 // from Joni, once it starts prefroming
  1176.                                 // UTF-8 content validation.
  1177.                                 result.append(
  1178.                                         runtime.newFixnum(utf8Decode(encode)));
  1179.                             } catch (IllegalArgumentException e) {
  1180.                                 throw runtime.newArgumentError(e.getMessage());
  1181.                             }
  1182.                         }
  1183.                     }
  1184.                     break;
  1185.                  case 'X':
  1186.                      if (occurrences == IS_STAR) {
  1187.                          // MRI behavior: Contrary to what seems to be logical,
  1188.                          // when '*' is given, MRI calculates the distance
  1189.                          // to the end, in order to go backwards.
  1190.                          occurrences = /*encode.limit() - */encode.remaining();
  1191.                      }

  1192.                      try {
  1193.                          encode.position(encode.position() - occurrences);
  1194.                      } catch (IllegalArgumentException e) {
  1195.                          throw runtime.newArgumentError("in `unpack': X outside of string");
  1196.                      }
  1197.                      break;
  1198.                  case 'x':
  1199.                       if (occurrences == IS_STAR) {
  1200.                            occurrences = encode.remaining();
  1201.                       }

  1202.                       try {
  1203.                           encode.position(encode.position() + occurrences);
  1204.                       } catch (IllegalArgumentException e) {
  1205.                           throw runtime.newArgumentError("in `unpack': x outside of string");
  1206.                       }

  1207.                      break;
  1208.                 case 'w':
  1209.                     if (occurrences == IS_STAR || occurrences > encode.remaining()) {
  1210.                         occurrences = encode.remaining();
  1211.                     }

  1212.                     long ul = 0;
  1213.                     long ulmask = (0xfe << 56) & 0xffffffff;
  1214.                     RubyBignum big128 = RubyBignum.newBignum(runtime, 128);
  1215.                     int pos = encode.position();

  1216.                     while (occurrences > 0 && pos < encode.limit()) {
  1217.                         ul <<= 7;
  1218.                         ul |= encode.get(pos) & 0x7f;
  1219.                         if((encode.get(pos++) & 0x80) == 0) {
  1220.                             result.append(RubyFixnum.newFixnum(runtime, ul));
  1221.                             occurrences--;
  1222.                             ul = 0;
  1223.                         } else if((ul & ulmask) == 0) {
  1224.                             RubyBignum big = RubyBignum.newBignum(runtime, ul);
  1225.                             while(occurrences > 0 && pos < encode.limit()) {
  1226.                                 IRubyObject mulResult = big.op_mul(runtime.getCurrentContext(), big128);
  1227.                                 IRubyObject v = mulResult.callMethod(runtime.getCurrentContext(), "+",
  1228.                                         RubyBignum.newBignum(runtime, encode.get(pos) & 0x7f));
  1229.                                 if(v instanceof RubyFixnum) {
  1230.                                     big = RubyBignum.newBignum(runtime, RubyNumeric.fix2long(v));
  1231.                                 } else if (v instanceof RubyBignum) {
  1232.                                     big = (RubyBignum)v;
  1233.                                 }
  1234.                                 if((encode.get(pos++) & 0x80) == 0) {
  1235.                                     result.add(RubyBignum.bignorm(runtime, big.getValue()));
  1236.                                     occurrences--;
  1237.                                     ul = 0;
  1238.                                     break;
  1239.                                 }
  1240.                             }
  1241.                         }
  1242.                     }
  1243.                     try {
  1244.                         encode.position(pos);
  1245.                     } catch (IllegalArgumentException e) {
  1246.                         throw runtime.newArgumentError("in `unpack': poorly encoded input");
  1247.                     }
  1248.                     break;
  1249.             }
  1250.         }
  1251.         return result;
  1252.     }

  1253.     /** rb_uv_to_utf8
  1254.      *
  1255.      */
  1256.     public static int utf8Decode(Ruby runtime, byte[]to, int p, int code) {
  1257.         if (code <= 0x7f) {
  1258.             to[p] = (byte)code;
  1259.             return 1;
  1260.         }
  1261.         if (code <= 0x7ff) {
  1262.             to[p + 0] = (byte)(((code >>> 6) & 0xff) | 0xc0);
  1263.             to[p + 1] = (byte)((code & 0x3f) | 0x80);
  1264.             return 2;
  1265.         }
  1266.         if (code <= 0xffff) {
  1267.             to[p + 0] = (byte)(((code >>> 12) & 0xff) | 0xe0);
  1268.             to[p + 1] = (byte)(((code >>> 6) & 0x3f) | 0x80);
  1269.             to[p + 2] = (byte)((code & 0x3f) | 0x80);
  1270.             return 3;
  1271.         }
  1272.         if (code <= 0x1fffff) {
  1273.             to[p + 0] = (byte)(((code >>> 18) & 0xff) | 0xf0);
  1274.             to[p + 1] = (byte)(((code >>> 12) & 0x3f) | 0x80);
  1275.             to[p + 2] = (byte)(((code >>> 6) & 0x3f) | 0x80);
  1276.             to[p + 3] = (byte)((code & 0x3f) | 0x80);
  1277.             return 4;
  1278.         }
  1279.         if (code <= 0x3ffffff) {
  1280.             to[p + 0] = (byte)(((code >>> 24) & 0xff) | 0xf8);
  1281.             to[p + 1] = (byte)(((code >>> 18) & 0x3f) | 0x80);
  1282.             to[p + 2] = (byte)(((code >>> 12) & 0x3f) | 0x80);
  1283.             to[p + 3] = (byte)(((code >>> 6) & 0x3f) | 0x80);
  1284.             to[p + 4] = (byte)((code & 0x3f) | 0x80);
  1285.             return 5;
  1286.         }
  1287.         if (code <= 0x7fffffff) {
  1288.             to[p + 0] = (byte)(((code >>> 30) & 0xff) | 0xfc);
  1289.             to[p + 1] = (byte)(((code >>> 24) & 0x3f) | 0x80);
  1290.             to[p + 2] = (byte)(((code >>> 18) & 0x3f) | 0x80);
  1291.             to[p + 3] = (byte)(((code >>> 12) & 0x3f) | 0x80);
  1292.             to[p + 4] = (byte)(((code >>> 6) & 0x3f) | 0x80);
  1293.             to[p + 5] = (byte)((code & 0x3f) | 0x80);
  1294.             return 6;
  1295.         }
  1296.         throw runtime.newRangeError("pack(U): value out of range");
  1297.     }

  1298.     /** utf8_to_uv
  1299.      */
  1300.     private static int utf8Decode(ByteBuffer buffer) {        
  1301.         int c = buffer.get() & 0xFF;
  1302.         int uv = c;
  1303.         int n;

  1304.         if ((c & 0x80) == 0) {
  1305.             return c;
  1306.         }

  1307.         if ((c & 0x40) == 0) {
  1308.             throw new IllegalArgumentException("malformed UTF-8 character");
  1309.         }
  1310.        
  1311.       if      ((uv & 0x20) == 0) { n = 2; uv &= 0x1f; }
  1312.       else if ((uv & 0x10) == 0) { n = 3; uv &= 0x0f; }
  1313.       else if ((uv & 0x08) == 0) { n = 4; uv &= 0x07; }
  1314.       else if ((uv & 0x04) == 0) { n = 5; uv &= 0x03; }
  1315.       else if ((uv & 0x02) == 0) { n = 6; uv &= 0x01; }
  1316.       else {
  1317.           throw new IllegalArgumentException("malformed UTF-8 character");
  1318.       }
  1319.       if (n > buffer.remaining() + 1) {
  1320.           throw new IllegalArgumentException(
  1321.                   "malformed UTF-8 character (expected " + n + " bytes, "
  1322.                   + "given " + (buffer.remaining() + 1)  + " bytes)");
  1323.       }

  1324.       int limit = n - 1;

  1325.       n--;

  1326.       if (n != 0) {
  1327.           while (n-- != 0) {
  1328.               c = buffer.get() & 0xff;
  1329.               if ((c & 0xc0) != 0x80) {
  1330.                   throw new IllegalArgumentException("malformed UTF-8 character");
  1331.               }
  1332.               else {
  1333.                   c &= 0x3f;
  1334.                   uv = uv << 6 | c;
  1335.               }
  1336.           }
  1337.       }

  1338.       if (uv < utf8_limits[limit]) {
  1339.           throw new IllegalArgumentException("redundant UTF-8 sequence");
  1340.       }

  1341.       return uv;
  1342.     }

  1343.     private static final long utf8_limits[] = {
  1344.         0x0,                        /* 1 */
  1345.         0x80,                       /* 2 */
  1346.         0x800,                      /* 3 */
  1347.         0x10000,                    /* 4 */
  1348.         0x200000,                   /* 5 */
  1349.         0x4000000,                  /* 6 */
  1350.         0x80000000,                 /* 7 */
  1351.     };

  1352.     private static int safeGet(ByteBuffer encode) {
  1353.         while (encode.hasRemaining()) {
  1354.             int got = encode.get() & 0xff;
  1355.            
  1356.             if (got != 0) return got;
  1357.         }
  1358.        
  1359.         return 0;
  1360.     }

  1361.     private static int safeGetIgnoreNull(ByteBuffer encode) {
  1362.         int next = 0;
  1363.         while (encode.hasRemaining() && next == 0) {
  1364.             next = safeGet(encode);
  1365.         }
  1366.         return next;
  1367.     }

  1368.     public static void decode(Ruby runtime, ByteBuffer encode, int occurrences,
  1369.             RubyArray result, Converter converter) {
  1370.         int lPadLength = 0;

  1371.         if (occurrences == IS_STAR) {
  1372.             occurrences = encode.remaining() / converter.size;
  1373.         } else if (occurrences > encode.remaining() / converter.size) {
  1374.             lPadLength = occurrences - encode.remaining() / converter.size;
  1375.             occurrences = encode.remaining() / converter.size;
  1376.         }
  1377.         for (; occurrences-- > 0;) {
  1378.             result.append(converter.decode(runtime, encode));
  1379.         }

  1380.         for (; lPadLength-- > 0;)
  1381.             result.append(runtime.getNil());
  1382.     }

  1383.     public static int encode(Ruby runtime, int occurrences, ByteList result,
  1384.             RubyArray list, int index, ConverterExecutor converter) {
  1385.         int listSize = list.size();

  1386.         while (occurrences-- > 0) {
  1387.             if (listSize-- <= 0 || index >= list.size()) {
  1388.                 throw runtime.newArgumentError(sTooFew);
  1389.             }

  1390.             IRubyObject from = list.eltInternal(index++);

  1391.             converter.encode(runtime, from, result);
  1392.         }

  1393.         return index;
  1394.     }

  1395.     private abstract static class ConverterExecutor {
  1396.         protected Converter converter;
  1397.         public void setConverter(Converter converter) {
  1398.             this.converter = converter;
  1399.         }

  1400.         public abstract IRubyObject decode(Ruby runtime, ByteBuffer format);
  1401.         public abstract void encode(Ruby runtime, IRubyObject from, ByteList result);
  1402.     }

  1403.     private static ConverterExecutor executor() {
  1404.         return new ConverterExecutor() {
  1405.             @Override
  1406.             public IRubyObject decode(Ruby runtime, ByteBuffer format) {
  1407.                 return converter.decode(runtime, format);
  1408.             }

  1409.             @Override
  1410.             public void encode(Ruby runtime, IRubyObject from, ByteList result) {
  1411.                 if (from == runtime.getNil() && converter.getType() != null) throw runtime.newTypeError(from, converter.getType());                
  1412.                 converter.encode(runtime, from, result);
  1413.             }
  1414.         };
  1415.     }

  1416.     public abstract static class Converter {
  1417.         public int size;
  1418.         public String type;

  1419.         public Converter(int size) {
  1420.             this(size, null);
  1421.         }
  1422.        
  1423.         public Converter(int size, String type) {
  1424.             this.size = size;
  1425.             this.type = type;
  1426.         }
  1427.        
  1428.         public String getType() {
  1429.             return type;
  1430.         }

  1431.         public abstract IRubyObject decode(Ruby runtime, ByteBuffer format);
  1432.         public abstract void encode(Ruby runtime, IRubyObject from, ByteList result);
  1433.     }

  1434.     private abstract static class QuadConverter extends Converter{
  1435.         public QuadConverter(int size, String type) {
  1436.             super(size, type);
  1437.         }
  1438.        
  1439.         public QuadConverter(int size) {
  1440.             super(size);
  1441.         }

  1442.         protected int overflowQuad(long quad) {
  1443.             return (int) (quad & 0xffff);
  1444.         }

  1445.         protected void encodeShortByByteOrder(ByteList result, int s) {
  1446.             if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
  1447.                 encodeShortBigEndian(result, s);
  1448.             } else {
  1449.                 encodeShortLittleEndian(result, s);
  1450.             }
  1451.         }

  1452.         protected void encodeLongByByteOrder(ByteList result, long l) {
  1453.             if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
  1454.                 encodeLongBigEndian(result, l);
  1455.             } else {
  1456.                 encodeLongLittleEndian(result, l);
  1457.             }
  1458.         }
  1459.     }

  1460.     /**
  1461.      * shrinks a stringbuffer.
  1462.      * shrinks a stringbuffer by a number of characters.
  1463.      * @param i2Shrink the stringbuffer
  1464.      * @param iLength how much to shrink
  1465.      * @return the stringbuffer
  1466.      **/
  1467.     private static final ByteList shrink(ByteList i2Shrink, int iLength) {
  1468.         iLength = i2Shrink.length() - iLength;

  1469.         if (iLength < 0) {
  1470.             throw new IllegalArgumentException();
  1471.         }
  1472.         i2Shrink.length(iLength);
  1473.         return i2Shrink;
  1474.     }

  1475.     /**
  1476.      * grows a stringbuffer.
  1477.      * uses the Strings to pad the buffer for a certain length
  1478.      * @param i2Grow the buffer to grow
  1479.      * @param iPads the string used as padding
  1480.      * @param iLength how much padding is needed
  1481.      * @return the padded buffer
  1482.      **/
  1483.     private static final ByteList grow(ByteList i2Grow, byte[]iPads, int iLength) {
  1484.         int lPadLength = iPads.length;
  1485.         while (iLength >= lPadLength) {
  1486.             i2Grow.append(iPads);
  1487.             iLength -= lPadLength;
  1488.         }
  1489.         i2Grow.append(iPads, 0, iLength);
  1490.         return i2Grow;
  1491.     }

  1492.     /**
  1493.      * Same as pack but defaults tainting of output to false.
  1494.      */
  1495.     public static RubyString pack(Ruby runtime, RubyArray list, ByteList formatString) {
  1496.         return packCommon(runtime, list, formatString, false, executor());
  1497.     }

  1498.     public static RubyString pack(ThreadContext context, Ruby runtime, RubyArray list, RubyString formatString) {
  1499.         RubyString pack = packCommon(runtime, list, formatString.getByteList(), formatString.isTaint(), executor());
  1500.         return (RubyString) pack.infectBy(formatString);
  1501.     }

  1502.     private static RubyString packCommon(Ruby runtime, RubyArray list, ByteList formatString, boolean tainted, ConverterExecutor executor) {
  1503.         ByteBuffer format = ByteBuffer.wrap(formatString.getUnsafeBytes(), formatString.begin(), formatString.length());
  1504.         ByteList result = new ByteList();
  1505.         boolean taintOutput = tainted;
  1506.         int listSize = list.size();
  1507.         int type = 0;
  1508.         int next = safeGet(format);

  1509.         int idx = 0;
  1510.         ByteList lCurElemString;

  1511.         int enc_info = 1;

  1512.         mainLoop: while (next != 0) {
  1513.             type = next;
  1514.             next = safeGet(format);
  1515.             if (PACK_IGNORE_NULL_CODES.indexOf(type) != -1 && next == 0) {
  1516.                 next = safeGetIgnoreNull(format);
  1517.             }

  1518.             // Skip all whitespace in pack format string
  1519.             while (ASCII.isSpace(type)) {
  1520.                 if (next == 0) break mainLoop;
  1521.                 type = next;
  1522.                 next = safeGet(format);
  1523.             }

  1524.             // Skip embedded comments in pack format string
  1525.             if (type == '#') {
  1526.                 while (type != '\n') {
  1527.                     if (next == 0) break mainLoop;
  1528.                     type = next;
  1529.                     next = safeGet(format);
  1530.                 }
  1531.             }

  1532.             if (next == '!' || next == '_') {
  1533.                 int index = NATIVE_CODES.indexOf(type);
  1534.                 if (index == -1) {
  1535.                     throw runtime.newArgumentError("'" + next +
  1536.                             "' allowed only after types " + NATIVE_CODES);
  1537.                 }
  1538.                 int typeBeforeMap = type;
  1539.                 type = MAPPED_CODES.charAt(index);

  1540.                 next = safeGet(format);
  1541.                 if (PACK_IGNORE_NULL_CODES_WITH_MODIFIERS.indexOf(typeBeforeMap) != -1 && next == 0) {
  1542.                     next = safeGetIgnoreNull(format);
  1543.                 }
  1544.             }
  1545.            
  1546.             if (next == '>' || next == '<') {
  1547.                 next = next == '>' ? BE : LE;
  1548.                 int index = ENDIANESS_CODES.indexOf(type + next);
  1549.                 if (index == -1) {
  1550.                     throw runtime.newArgumentError("'" + (char)next +
  1551.                             "' allowed only after types sSiIlLqQ");
  1552.                 }
  1553.                 type = ENDIANESS_CODES.charAt(index);
  1554.                 next = safeGet(format);
  1555.             }

  1556.             // Determine how many of type are needed (default: 1)
  1557.             int occurrences = 1;
  1558.             boolean isStar = false;
  1559.             boolean ignoreStar = false;
  1560.             if (next != 0) {
  1561.                 if (next == '*') {
  1562.                     if ("@XxumM".indexOf(type) != -1) {
  1563.                         occurrences = 0;
  1564.                         ignoreStar = true;
  1565.                     } else {
  1566.                         occurrences = list.size() - idx;
  1567.                         isStar = true;
  1568.                     }
  1569.                     next = safeGet(format);
  1570.                 } else if (ASCII.isDigit(next)) {
  1571.                     occurrences = 0;
  1572.                     do {
  1573.                         occurrences = occurrences * 10 + Character.digit((char)(next & 0xFF), 10);
  1574.                         next = safeGet(format);
  1575.                     } while (next != 0 && ASCII.isDigit(next));
  1576.                 }
  1577.             }

  1578.             switch (type) {
  1579.                 case 'U':
  1580.                     if (enc_info == 1) enc_info = 2;
  1581.                     break;
  1582.                 case 'm':
  1583.                 case 'M':
  1584.                 case 'u':
  1585.                     break;
  1586.                 default:
  1587.                     enc_info = 0;
  1588.                     break;
  1589.             }

  1590.             Converter converter = converters[type];

  1591.             if (converter != null) {
  1592.                 executor.setConverter(converter);
  1593.                 idx = encode(runtime, occurrences, result, list, idx, executor);
  1594.                 continue;
  1595.             }

  1596.             switch (type) {
  1597.                 case '%' :
  1598.                     throw runtime.newArgumentError("% is not supported");
  1599.                 case 'A' :
  1600.                 case 'a' :
  1601.                 case 'Z' :
  1602.                 case 'B' :
  1603.                 case 'b' :
  1604.                 case 'H' :
  1605.                 case 'h' :
  1606.                     {
  1607.                         if (listSize-- <= 0) {
  1608.                             throw runtime.newArgumentError(sTooFew);
  1609.                         }

  1610.                         IRubyObject from = list.eltInternal(idx++);
  1611.                         if(from.isTaint()) taintOutput = true;

  1612.                         lCurElemString = from == runtime.getNil() ? ByteList.EMPTY_BYTELIST : from.convertToString().getByteList();

  1613.                         if (isStar) {
  1614.                             occurrences = lCurElemString.length();
  1615.                             // 'Z' adds extra null pad (versus 'a')
  1616.                             if (type == 'Z') occurrences++;
  1617.                         }

  1618.                         switch (type) {
  1619.                             case 'a' :
  1620.                             case 'A' :
  1621.                             case 'Z' :
  1622.                                 if (lCurElemString.length() >= occurrences) {
  1623.                                     result.append(lCurElemString.getUnsafeBytes(), lCurElemString.getBegin(), occurrences);
  1624.                                 } else {//need padding
  1625.                                     //I'm fairly sure there is a library call to create a
  1626.                                     //string filled with a given char with a given length but I couldn't find it
  1627.                                     result.append(lCurElemString);
  1628.                                     occurrences -= lCurElemString.length();

  1629.                                     switch (type) {
  1630.                                       case 'a':
  1631.                                       case 'Z':
  1632.                                           grow(result, sNil10, occurrences);
  1633.                                           break;
  1634.                                       default:
  1635.                                           grow(result, sSp10, occurrences);
  1636.                                           break;
  1637.                                     }
  1638.                                 }
  1639.                             break;
  1640.                             case 'b' :
  1641.                                 {
  1642.                                     int currentByte = 0;
  1643.                                     int padLength = 0;

  1644.                                     if (occurrences > lCurElemString.length()) {
  1645.                                         padLength = (occurrences - lCurElemString.length()) / 2 + (occurrences + lCurElemString.length()) % 2;
  1646.                                         occurrences = lCurElemString.length();
  1647.                                     }

  1648.                                     for (int i = 0; i < occurrences;) {
  1649.                                         if ((lCurElemString.charAt(i++) & 1) != 0) {//if the low bit is set
  1650.                                             currentByte |= 128; //set the high bit of the result
  1651.                                         }

  1652.                                         if ((i & 7) == 0) {
  1653.                                             result.append((byte) (currentByte & 0xff));
  1654.                                             currentByte = 0;
  1655.                                             continue;
  1656.                                         }

  1657.                                            //if the index is not a multiple of 8, we are not on a byte boundary
  1658.                                            currentByte >>= 1; //shift the byte
  1659.                                     }

  1660.                                     if ((occurrences & 7) != 0) { //if the length is not a multiple of 8
  1661.                                         currentByte >>= 7 - (occurrences & 7); //we need to pad the last byte
  1662.                                         result.append((byte) (currentByte & 0xff));
  1663.                                     }

  1664.                                     //do some padding, I don't understand the padding strategy
  1665.                                     result.length(result.length() + padLength);
  1666.                                 }
  1667.                             break;
  1668.                             case 'B' :
  1669.                                 {
  1670.                                     int currentByte = 0;
  1671.                                     int padLength = 0;

  1672.                                     if (occurrences > lCurElemString.length()) {
  1673.                                         padLength = (occurrences - lCurElemString.length()) / 2 + (occurrences + lCurElemString.length()) % 2;
  1674.                                         occurrences = lCurElemString.length();
  1675.                                     }

  1676.                                     for (int i = 0; i < occurrences;) {
  1677.                                         currentByte |= lCurElemString.charAt(i++) & 1;

  1678.                                         // we filled up current byte; append it and create next one
  1679.                                         if ((i & 7) == 0) {
  1680.                                             result.append((byte) (currentByte & 0xff));
  1681.                                             currentByte = 0;
  1682.                                             continue;
  1683.                                         }

  1684.                                         //if the index is not a multiple of 8, we are not on a byte boundary
  1685.                                         currentByte <<= 1;
  1686.                                     }

  1687.                                     if ((occurrences & 7) != 0) { //if the length is not a multiple of 8
  1688.                                         currentByte <<= 7 - (occurrences & 7); //we need to pad the last byte
  1689.                                         result.append((byte) (currentByte & 0xff));
  1690.                                     }

  1691.                                     result.length(result.length() + padLength);
  1692.                                 }
  1693.                             break;
  1694.                             case 'h' :
  1695.                                 {
  1696.                                     int currentByte = 0;
  1697.                                     int padLength = 0;

  1698.                                     if (occurrences > lCurElemString.length()) {
  1699.                                         padLength = occurrences - lCurElemString.length() + 1;
  1700.                                         occurrences = lCurElemString.length();
  1701.                                     }

  1702.                                     for (int i = 0; i < occurrences;) {
  1703.                                         byte currentChar = (byte)lCurElemString.charAt(i++);

  1704.                                         if (Character.isJavaIdentifierStart(currentChar)) {
  1705.                                             //this test may be too lax but it is the same as in MRI
  1706.                                             currentByte |= (((currentChar & 15) + 9) & 15) << 4;
  1707.                                         } else {
  1708.                                             currentByte |= (currentChar & 15) << 4;
  1709.                                         }

  1710.                                         if ((i & 1) != 0) {
  1711.                                             currentByte >>= 4;
  1712.                                         } else {
  1713.                                             result.append((byte) (currentByte & 0xff));
  1714.                                             currentByte = 0;
  1715.                                         }
  1716.                                     }

  1717.                                     if ((occurrences & 1) != 0) {
  1718.                                         result.append((byte) (currentByte & 0xff));
  1719.                                         if(padLength > 0) {
  1720.                                             padLength--;
  1721.                                         }
  1722.                                     }

  1723.                                     result.length(result.length() + padLength / 2);
  1724.                                 }
  1725.                             break;
  1726.                             case 'H' :
  1727.                                 {
  1728.                                     int currentByte = 0;
  1729.                                     int padLength = 0;

  1730.                                     if (occurrences > lCurElemString.length()) {
  1731.                                         padLength = occurrences - lCurElemString.length() + 1;
  1732.                                         occurrences = lCurElemString.length();
  1733.                                     }

  1734.                                     for (int i = 0; i < occurrences;) {
  1735.                                         byte currentChar = (byte)lCurElemString.charAt(i++);

  1736.                                         if (Character.isJavaIdentifierStart(currentChar)) {
  1737.                                             //this test may be too lax but it is the same as in MRI
  1738.                                             currentByte |= ((currentChar & 15) + 9) & 15;
  1739.                                         } else {
  1740.                                             currentByte |= currentChar & 15;
  1741.                                         }

  1742.                                         if ((i & 1) != 0) {
  1743.                                             currentByte <<= 4;
  1744.                                         } else {
  1745.                                             result.append((byte) (currentByte & 0xff));
  1746.                                             currentByte = 0;
  1747.                                         }
  1748.                                     }

  1749.                                     if ((occurrences & 1) != 0) {
  1750.                                         result.append((byte) (currentByte & 0xff));
  1751.                                         if(padLength > 0) {
  1752.                                             padLength--;
  1753.                                         }
  1754.                                     }

  1755.                                     result.length(result.length() + padLength / 2);
  1756.                                 }
  1757.                             break;
  1758.                         }
  1759.                         break;
  1760.                     }

  1761.                 case 'x' :
  1762.                     grow(result, sNil10, occurrences);
  1763.                     break;
  1764.                 case 'X' :
  1765.                     try {
  1766.                         shrink(result, occurrences);
  1767.                     } catch (IllegalArgumentException e) {
  1768.                         throw runtime.newArgumentError("in `pack': X outside of string");
  1769.                     }
  1770.                     break;
  1771.                 case '@' :
  1772.                     occurrences -= result.length();
  1773.                     if (occurrences > 0) {
  1774.                         grow(result, sNil10, occurrences);
  1775.                     }
  1776.                     occurrences = -occurrences;
  1777.                     if (occurrences > 0) {
  1778.                         shrink(result, occurrences);
  1779.                     }
  1780.                     break;
  1781.                 case 'u' :
  1782.                 case 'm' : {
  1783.                         if (listSize-- <= 0) throw runtime.newArgumentError(sTooFew);

  1784.                         IRubyObject from = list.eltInternal(idx++);
  1785.                         if (from == runtime.getNil()) throw runtime.newTypeError(from, "Integer");
  1786.                         lCurElemString = from.convertToString().getByteList();
  1787.                         if (occurrences == 0 && type == 'm' && !ignoreStar) {
  1788.                             encodes(runtime, result, lCurElemString.getUnsafeBytes(),
  1789.                                     lCurElemString.getBegin(), lCurElemString.length(),
  1790.                                     lCurElemString.length(), (byte)type, false);
  1791.                             break;
  1792.                         }

  1793.                         occurrences = occurrences <= 2 ? 45 : occurrences / 3 * 3;
  1794.                         if (lCurElemString.length() == 0) break;

  1795.                         byte[] charsToEncode = lCurElemString.getUnsafeBytes();
  1796.                         for (int i = 0; i < lCurElemString.length(); i += occurrences) {
  1797.                             encodes(runtime, result, charsToEncode,
  1798.                                     i + lCurElemString.getBegin(), lCurElemString.length() - i,
  1799.                                     occurrences, (byte)type, true);
  1800.                         }
  1801.                     }
  1802.                     break;
  1803.                 case 'M' : {
  1804.                        if (listSize-- <= 0) throw runtime.newArgumentError(sTooFew);

  1805.                        IRubyObject from = list.eltInternal(idx++);
  1806.                        lCurElemString = from == runtime.getNil() ? ByteList.EMPTY_BYTELIST : from.asString().getByteList();

  1807.                        if (occurrences <= 1) {
  1808.                            occurrences = 72;
  1809.                        }

  1810.                        qpencode(result, lCurElemString, occurrences);
  1811.                     }
  1812.                     break;
  1813.                 case 'U' :
  1814.                     while (occurrences-- > 0) {
  1815.                         if (listSize-- <= 0) throw runtime.newArgumentError(sTooFew);

  1816.                         IRubyObject from = list.eltInternal(idx++);
  1817.                         int code = from == runtime.getNil() ? 0 : RubyNumeric.num2int(from);

  1818.                         if (code < 0) throw runtime.newRangeError("pack(U): value out of range");

  1819.                         result.ensure(result.getRealSize() + 6);
  1820.                         result.setRealSize(result.getRealSize() + utf8Decode(runtime, result.getUnsafeBytes(), result.getBegin() + result.getRealSize(), code));
  1821.                     }
  1822.                     break;
  1823.                 case 'w' :
  1824.                     while (occurrences-- > 0) {
  1825.                         if (listSize-- <= 0) throw runtime.newArgumentError(sTooFew);

  1826.                         ByteList buf = new ByteList();
  1827.                         IRubyObject from = list.eltInternal(idx++);

  1828.                         if (from.isNil()) throw runtime.newTypeError("pack('w') does not take nil");


  1829.                         if (from instanceof RubyBignum) {
  1830.                             RubyBignum big128 = RubyBignum.newBignum(runtime, 128);
  1831.                             while (from instanceof RubyBignum) {
  1832.                                 RubyBignum bignum = (RubyBignum)from;
  1833.                                 RubyArray ary = (RubyArray)bignum.divmod(runtime.getCurrentContext(), big128);
  1834.                                 buf.append((byte)(RubyNumeric.fix2int(ary.at(RubyFixnum.one(runtime))) | 0x80) & 0xff);
  1835.                                 from = ary.at(RubyFixnum.zero(runtime));
  1836.                             }
  1837.                         }

  1838.                         long l = RubyNumeric.num2long(from);

  1839.                         // we don't deal with negatives.
  1840.                         if (l >= 0) {

  1841.                             while(l != 0) {
  1842.                                 buf.append((byte)(((l & 0x7f) | 0x80) & 0xff));
  1843.                                 l >>= 7;
  1844.                             }

  1845.                             int left = 0;
  1846.                             int right = buf.getRealSize() - 1;

  1847.                             if (right >= 0) {
  1848.                                 buf.getUnsafeBytes()[0] &= 0x7F;
  1849.                             } else {
  1850.                                 buf.append(0);
  1851.                             }

  1852.                             while (left < right) {
  1853.                                 byte tmp = buf.getUnsafeBytes()[left];
  1854.                                 buf.getUnsafeBytes()[left] = buf.getUnsafeBytes()[right];
  1855.                                 buf.getUnsafeBytes()[right] = tmp;

  1856.                                 left++;
  1857.                                 right--;
  1858.                             }

  1859.                             result.append(buf);
  1860.                         } else {
  1861.                             throw runtime.newArgumentError("can't compress negative numbers");
  1862.                         }
  1863.                     }

  1864.                     break;
  1865.             }
  1866.         }        

  1867.         RubyString output = runtime.newString(result);
  1868.         if (taintOutput) output.taint(runtime.getCurrentContext());

  1869.         switch (enc_info)
  1870.         {
  1871.             case 1:
  1872.                 output.setEncodingAndCodeRange(USASCII, RubyObject.USER8_F);
  1873.                 break;
  1874.             case 2:
  1875.                 output.force_encoding(runtime.getCurrentContext(),
  1876.                         runtime.getEncodingService().convertEncodingToRubyEncoding(UTF8));
  1877.                 break;
  1878.             default:
  1879.                 /* do nothing, keep ASCII-8BIT */
  1880.         }

  1881.         return output;
  1882.     }

  1883.     /**
  1884.      * Retrieve an encoded int in little endian starting at index in the
  1885.      * string value.
  1886.      *
  1887.      * @param encode string to get int from
  1888.      * @return the decoded integer
  1889.      */
  1890.     private static int decodeIntLittleEndian(ByteBuffer encode) {
  1891.         encode.order(ByteOrder.LITTLE_ENDIAN);
  1892.         int value = encode.getInt();
  1893.         encode.order(ByteOrder.BIG_ENDIAN);
  1894.         return value;
  1895.     }

  1896.     /**
  1897.      * Retrieve an encoded int in little endian starting at index in the
  1898.      * string value.
  1899.      *
  1900.      * @param encode string to get int from
  1901.      * @return the decoded integer
  1902.      */
  1903.     private static int decodeIntBigEndian(ByteBuffer encode) {
  1904.         return encode.getInt();
  1905.     }

  1906.     /**
  1907.      * Retrieve an encoded int in big endian starting at index in the string
  1908.      * value.
  1909.      *
  1910.      * @param encode string to get int from
  1911.      * @return the decoded integer
  1912.      */
  1913.     private static long decodeIntUnsignedBigEndian(ByteBuffer encode) {
  1914.         return (long)encode.getInt() & 0xFFFFFFFFL;
  1915.     }

  1916.     /**
  1917.      * Retrieve an encoded int in little endian starting at index in the
  1918.      * string value.
  1919.      *
  1920.      * @param encode the encoded string
  1921.      * @return the decoded integer
  1922.      */
  1923.     private static long decodeIntUnsignedLittleEndian(ByteBuffer encode) {
  1924.         encode.order(ByteOrder.LITTLE_ENDIAN);
  1925.         long value = encode.getInt() & 0xFFFFFFFFL;
  1926.         encode.order(ByteOrder.BIG_ENDIAN);
  1927.         return value;
  1928.     }

  1929.     /**
  1930.      * Encode an int in little endian format into a packed representation.
  1931.      *
  1932.      * @param result to be appended to
  1933.      * @param s the integer to encode
  1934.      */
  1935.     private static void encodeIntLittleEndian(ByteList result, int s) {
  1936.         result.append((byte) (s & 0xff)).append((byte) ((s >> 8) & 0xff));
  1937.         result.append((byte) ((s>>16) & 0xff)).append((byte) ((s>>24) &0xff));
  1938.     }

  1939.     /**
  1940.      * Encode an int in big-endian format into a packed representation.
  1941.      *
  1942.      * @param result to be appended to
  1943.      * @param s the integer to encode
  1944.      */
  1945.     private static void encodeIntBigEndian(ByteList result, int s) {
  1946.         result.append((byte) ((s>>24) &0xff)).append((byte) ((s>>16) &0xff));
  1947.         result.append((byte) ((s >> 8) & 0xff)).append((byte) (s & 0xff));
  1948.     }

  1949.     /**
  1950.      * Decode a long in big-endian format from a packed value
  1951.      *
  1952.      * @param encode string to get int from
  1953.      * @return the long value
  1954.      */
  1955.     private static long decodeLongBigEndian(ByteBuffer encode) {
  1956.         int c1 = decodeIntBigEndian(encode);
  1957.         int c2 = decodeIntBigEndian(encode);

  1958.         return ((long) c1 << 32) + (c2 & 0xffffffffL);
  1959.     }

  1960.     /**
  1961.      * Decode a long in little-endian format from a packed value
  1962.      *
  1963.      * @param encode string to get int from
  1964.      * @return the long value
  1965.      */
  1966.     private static long decodeLongLittleEndian(ByteBuffer encode) {
  1967.         int c1 = decodeIntLittleEndian(encode);
  1968.         int c2 = decodeIntLittleEndian(encode);

  1969.         return ((long) c2 << 32) + (c1 & 0xffffffffL);
  1970.     }

  1971.     /**
  1972.      * Encode a long in little-endian format into a packed value
  1973.      *
  1974.      * @param result to pack long into
  1975.      * @param l is the long to encode
  1976.      */
  1977.     private static void encodeLongLittleEndian(ByteList result, long l) {
  1978.         encodeIntLittleEndian(result, (int) (l & 0xffffffff));
  1979.         encodeIntLittleEndian(result, (int) (l >>> 32));
  1980.     }

  1981.     /**
  1982.      * Encode a long in big-endian format into a packed value
  1983.      *
  1984.      * @param result to pack long into
  1985.      * @param l is the long to encode
  1986.      */
  1987.     private static void encodeLongBigEndian(ByteList result, long l) {
  1988.         encodeIntBigEndian(result, (int) (l >>> 32));
  1989.         encodeIntBigEndian(result, (int) (l & 0xffffffff));
  1990.     }

  1991.     /**
  1992.      * Decode a double from a packed value
  1993.      *
  1994.      * @param encode string to get int from
  1995.      * @return the double value
  1996.      */
  1997.     private static double decodeDoubleLittleEndian(ByteBuffer encode) {
  1998.         return Double.longBitsToDouble(decodeLongLittleEndian(encode));
  1999.     }

  2000.     /**
  2001.      * Decode a double in big-endian from a packed value
  2002.      *
  2003.      * @param encode string to get int from
  2004.      * @return the double value
  2005.      */
  2006.     private static double decodeDoubleBigEndian(ByteBuffer encode) {
  2007.         return Double.longBitsToDouble(decodeLongBigEndian(encode));
  2008.     }

  2009.     /**
  2010.      * Encode a double in little endian format into a packed value
  2011.      *
  2012.      * @param result to pack double into
  2013.      * @param d is the double to encode
  2014.      */
  2015.     private static void encodeDoubleLittleEndian(ByteList result, double d) {
  2016.         encodeLongLittleEndian(result, Double.doubleToRawLongBits(d));
  2017.     }

  2018.     /**
  2019.      * Encode a double in big-endian format into a packed value
  2020.      *
  2021.      * @param result to pack double into
  2022.      * @param d is the double to encode
  2023.      */
  2024.     private static void encodeDoubleBigEndian(ByteList result, double d) {
  2025.         encodeLongBigEndian(result, Double.doubleToRawLongBits(d));
  2026.     }

  2027.     /**
  2028.      * Decode a float in big-endian from a packed value
  2029.      *
  2030.      * @param encode string to get int from
  2031.      * @return the double value
  2032.      */
  2033.     private static float decodeFloatBigEndian(ByteBuffer encode) {
  2034.         return Float.intBitsToFloat(decodeIntBigEndian(encode));
  2035.     }

  2036.     /**
  2037.      * Decode a float in little-endian from a packed value
  2038.      *
  2039.      * @param encode string to get int from
  2040.      * @return the double value
  2041.      */
  2042.     private static float decodeFloatLittleEndian(ByteBuffer encode) {
  2043.         return Float.intBitsToFloat(decodeIntLittleEndian(encode));
  2044.     }

  2045.     /**
  2046.      * Encode a float in little endian format into a packed value
  2047.      * @param result to pack float into
  2048.      * @param f is the float to encode
  2049.      */
  2050.     private static void encodeFloatLittleEndian(ByteList result, float f) {
  2051.         encodeIntLittleEndian(result, Float.floatToRawIntBits(f));
  2052.     }

  2053.     /**
  2054.      * Encode a float in big-endian format into a packed value
  2055.      * @param result to pack float into
  2056.      * @param f is the float to encode
  2057.      */
  2058.     private static void encodeFloatBigEndian(ByteList result, float f) {
  2059.         encodeIntBigEndian(result, Float.floatToRawIntBits(f));
  2060.     }

  2061.     /**
  2062.      * Decode a short in little-endian from a packed value
  2063.      *
  2064.      * @param encode string to get int from
  2065.      * @return the short value
  2066.      */
  2067.     private static int decodeShortUnsignedLittleEndian(ByteBuffer encode) {
  2068.         encode.order(ByteOrder.LITTLE_ENDIAN);
  2069.         int value = encode.getShort() & 0xFFFF;
  2070.         encode.order(ByteOrder.BIG_ENDIAN);
  2071.         return value;
  2072.     }

  2073.     /**
  2074.      * Decode a short in big-endian from a packed value
  2075.      *
  2076.      * @param encode string to get int from
  2077.      * @return the short value
  2078.      */
  2079.     private static int decodeShortUnsignedBigEndian(ByteBuffer encode) {
  2080.         int value = encode.getShort() & 0xFFFF;
  2081.         return value;
  2082.     }

  2083.     /**
  2084.      * Decode a short in little-endian from a packed value
  2085.      *
  2086.      * @param encode string to get int from
  2087.      * @return the short value
  2088.      */
  2089.     private static int decodeShortLittleEndian(ByteBuffer encode) {
  2090.         encode.order(ByteOrder.LITTLE_ENDIAN);
  2091.         int value = encode.getShort();
  2092.         encode.order(ByteOrder.BIG_ENDIAN);
  2093.         return value;
  2094.     }

  2095.     /**
  2096.      * Decode a short in big-endian from a packed value
  2097.      *
  2098.      * @param encode string to get int from
  2099.      * @return the short value
  2100.      */
  2101.     private static short decodeShortBigEndian(ByteBuffer encode) {
  2102.         return encode.getShort();
  2103.     }

  2104.     /**
  2105.      * Encode an short in little endian format into a packed representation.
  2106.      *
  2107.      * @param result to be appended to
  2108.      * @param s the short to encode
  2109.      */
  2110.     private static void encodeShortLittleEndian(ByteList result, int s) {
  2111.         result.append((byte) (s & 0xff)).append((byte) ((s & 0xff00) >> 8));
  2112.     }

  2113.     /**
  2114.      * Encode an shortin big-endian format into a packed representation.
  2115.      *
  2116.      * @param result to be appended to
  2117.      * @param s the short to encode
  2118.      */
  2119.     private static void encodeShortBigEndian(ByteList result, int s) {
  2120.         result.append((byte) ((s & 0xff00) >> 8)).append((byte) (s & 0xff));
  2121.     }
  2122. }