1 35 package org.jruby.util; 36 37 import java.nio.ByteBuffer ; 38 import java.nio.ByteOrder ; 39 import java.nio.CharBuffer ; 40 import java.nio.charset.CharacterCodingException ; 41 import java.nio.charset.Charset ; 42 import java.nio.charset.CharsetDecoder ; 43 import java.nio.charset.CodingErrorAction ; 44 import java.util.List ; 45 46 import org.jruby.Ruby; 47 import org.jruby.RubyArray; 48 import org.jruby.RubyFloat; 49 import org.jruby.RubyKernel; 50 import org.jruby.RubyNumeric; 51 import org.jruby.RubyString; 52 import org.jruby.runtime.builtin.IRubyObject; 53 54 public class Pack { 55 private static final String sSp10 = " "; 56 private static final String sNil10 = "\000\000\000\000\000\000\000\000\000\000"; 57 private static final int IS_STAR = -1; 58 60 private static final String NATIVE_CODES = "sSiIlL"; 61 private static final String sTooFew = "too few arguments"; 62 private static final byte[] hex_table; 63 private static final byte[] uu_table; 64 private static final byte[] b64_table; 65 private static final byte[] sHexDigits; 66 private static final int[] b64_xtable = new int[256]; 67 private static final Converter[] converters = new Converter[256]; 68 69 static { 70 hex_table = ByteList.plain("0123456789ABCDEF"); 71 uu_table = 72 ByteList.plain("`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"); 73 b64_table = 74 ByteList.plain("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); 75 sHexDigits = ByteList.plain("0123456789abcdef0123456789ABCDEFx"); 76 77 for (int i = 0; i < 256; i++) { 79 b64_xtable[i] = -1; 80 } 81 for (int i = 0; i < 64; i++) { 82 b64_xtable[(int)b64_table[i]] = i; 83 } 84 converters['v'] = new Converter(2) { 86 public IRubyObject decode(Ruby runtime, ByteBuffer enc) { 87 return runtime.newFixnum( 88 decodeShortUnsignedLittleEndian(enc)); 89 } 90 public void encode(Ruby runtime, IRubyObject o, StringBuffer result){ 91 int s = o == runtime.getNil() ? 0 : (int) (RubyNumeric.num2long(o) & 0xffff); 92 encodeShortLittleEndian(result, s); 93 }}; 94 converters['e'] = new Converter(4) { 96 public IRubyObject decode(Ruby runtime, ByteBuffer enc) { 97 return RubyFloat.newFloat(runtime, decodeFloatLittleEndian(enc)); 98 } 99 public void encode(Ruby runtime, IRubyObject o, StringBuffer result){ 100 float f = o == runtime.getNil() ? 0 : (float) RubyKernel.new_float(o,o).convertToFloat().getDoubleValue(); 101 encodeFloatLittleEndian(result, f); 102 }}; 103 Converter tmp = new Converter(4) { 104 public IRubyObject decode(Ruby runtime, ByteBuffer enc) { 105 return RubyFloat.newFloat(runtime, decodeFloatBigEndian(enc)); 106 } 107 public void encode(Ruby runtime, IRubyObject o, StringBuffer result){ 108 float f = o == runtime.getNil() ? 0 : (float) RubyKernel.new_float(o,o).convertToFloat().getDoubleValue(); 109 encodeFloatBigEndian(result, f); 110 } 111 }; 112 converters['F'] = tmp; converters['f'] = tmp; converters['g'] = tmp; converters['E'] = new Converter(8) { 117 public IRubyObject decode(Ruby runtime, ByteBuffer enc) { 118 return RubyFloat.newFloat(runtime, decodeDoubleLittleEndian(enc)); 119 } 120 public void encode(Ruby runtime, IRubyObject o, StringBuffer result){ 121 double d = o == runtime.getNil() ? 0 : RubyKernel.new_float(o,o).convertToFloat().getDoubleValue(); 122 encodeDoubleLittleEndian(result, d); 123 }}; 124 tmp = new Converter(8) { 125 public IRubyObject decode(Ruby runtime, ByteBuffer enc) { 126 return RubyFloat.newFloat(runtime, decodeDoubleBigEndian(enc)); 127 } 128 public void encode(Ruby runtime, IRubyObject o, StringBuffer result){ 129 double d = o == runtime.getNil() ? 0 : RubyKernel.new_float(o,o).convertToFloat().getDoubleValue(); 130 encodeDoubleBigEndian(result, d); 131 } 132 }; 133 converters['D'] = tmp; converters['d'] = tmp; converters['G'] = tmp; converters['s'] = new Converter(2) { public IRubyObject decode(Ruby runtime, ByteBuffer enc) { 138 return runtime.newFixnum(decodeShortBigEndian(enc)); 139 } 140 public void encode(Ruby runtime, IRubyObject o, StringBuffer result){ 141 int s = o == runtime.getNil() ? 0 : (int) (RubyNumeric.num2long(o) & 0xffff); 142 encodeShortBigEndian(result, s); 143 }}; 144 tmp = new Converter(2) { 145 public IRubyObject decode(Ruby runtime, ByteBuffer enc) { 146 return runtime.newFixnum( 147 decodeShortUnsignedBigEndian(enc)); 148 } 149 public void encode(Ruby runtime, IRubyObject o, StringBuffer result){ 150 int s = o == runtime.getNil() ? 0 : (int) (RubyNumeric.num2long(o) & 0xffff); 151 encodeShortBigEndian(result, s); 152 } 153 }; 154 converters['S'] = tmp; converters['n'] = tmp; converters['c'] = new Converter(1) { public IRubyObject decode(Ruby runtime, ByteBuffer enc) { 158 int c = enc.get(); 159 return runtime.newFixnum(c > (char) 127 ? c-256 : c); 160 } 161 public void encode(Ruby runtime, IRubyObject o, StringBuffer result){ 162 char c = o == runtime.getNil() ? 0 : (char) (RubyNumeric.num2long(o) & 0xff); 163 result.append(c); 164 }}; 165 converters['C'] = new Converter(1) { public IRubyObject decode(Ruby runtime, ByteBuffer enc) { 167 return runtime.newFixnum(enc.get() & 0xFF); 168 } 169 public void encode(Ruby runtime, IRubyObject o, StringBuffer result){ 170 char c = o == runtime.getNil() ? 0 : (char) (RubyNumeric.num2long(o) & 0xff); 171 result.append(c); 172 }}; 173 converters['V'] = new Converter(4) { 175 public IRubyObject decode(Ruby runtime, ByteBuffer enc) { 176 return runtime.newFixnum( 177 decodeIntUnsignedLittleEndian(enc)); 178 } 179 public void encode(Ruby runtime, IRubyObject o, StringBuffer result){ 180 int s = o == runtime.getNil() ? 0 : (int) RubyNumeric.num2long(o); 181 encodeIntLittleEndian(result, s); 182 }}; 183 tmp = new Converter(4) { 184 public IRubyObject decode(Ruby runtime, ByteBuffer enc) { 185 return runtime.newFixnum( 186 decodeIntUnsignedBigEndian(enc)); 187 } 188 public void encode(Ruby runtime, IRubyObject o, StringBuffer result){ 189 int s = o == runtime.getNil() ? 0 : (int) RubyNumeric.num2long(o); 190 encodeIntBigEndian(result, s); 191 } 192 }; 193 converters['I'] = tmp; converters['L'] = tmp; converters['N'] = tmp; tmp = new Converter(4) { 197 public IRubyObject decode(Ruby runtime, ByteBuffer enc) { 198 return runtime.newFixnum(decodeIntBigEndian(enc)); 199 } 200 public void encode(Ruby runtime, IRubyObject o, StringBuffer result){ 201 int s = (o == runtime.getNil() ? 0 : 202 (int) (RubyNumeric.num2long(o))); 203 encodeIntBigEndian(result, s); 204 } 205 }; 206 converters['l'] = tmp; converters['i'] = tmp; } 209 210 219 private static StringBuffer encodes(Ruby runtime, StringBuffer io2Append,String stringToEncode,int charCount,char encodingType) { 220 charCount = charCount < stringToEncode.length() ? charCount 221 : stringToEncode.length(); 222 io2Append.ensureCapacity(charCount * 4 / 3 + 6); 223 int i = 0; 224 byte[] lTranslationTable = encodingType == 'u' ? uu_table : b64_table; 225 char lPadding; 226 char[] charsToEncode = stringToEncode.toCharArray(); 227 if (encodingType == 'u') { 228 if (charCount >= lTranslationTable.length) { 229 throw runtime.newArgumentError( 230 "" 231 + charCount 232 + " is not a correct value for the number of bytes per line in a u directive. Correct values range from 0 to " 233 + lTranslationTable.length); 234 } 235 io2Append.append((char)lTranslationTable[charCount]); 236 lPadding = '`'; 237 } else { 238 lPadding = '='; 239 } 240 while (charCount >= 3) { 241 char lCurChar = charsToEncode[i++]; 242 char lNextChar = charsToEncode[i++]; 243 char lNextNextChar = charsToEncode[i++]; 244 io2Append.append((char)lTranslationTable[077 & (lCurChar >>> 2)]); 245 io2Append.append((char)lTranslationTable[077 & (((lCurChar << 4) & 060) | ((lNextChar >>> 4) & 017))]); 246 io2Append.append((char)lTranslationTable[077 & (((lNextChar << 2) & 074) | ((lNextNextChar >>> 6) & 03))]); 247 io2Append.append((char)lTranslationTable[077 & lNextNextChar]); 248 charCount -= 3; 249 } 250 if (charCount == 2) { 251 char lCurChar = charsToEncode[i++]; 252 char lNextChar = charsToEncode[i++]; 253 io2Append.append((char)lTranslationTable[077 & (lCurChar >>> 2)]); 254 io2Append.append((char)lTranslationTable[077 & (((lCurChar << 4) & 060) | ((lNextChar >> 4) & 017))]); 255 io2Append.append((char)lTranslationTable[077 & (((lNextChar << 2) & 074) | (('\0' >> 6) & 03))]); 256 io2Append.append(lPadding); 257 } else if (charCount == 1) { 258 char lCurChar = charsToEncode[i++]; 259 io2Append.append((char)lTranslationTable[077 & (lCurChar >>> 2)]); 260 io2Append.append((char)lTranslationTable[077 & (((lCurChar << 4) & 060) | (('\0' >>> 4) & 017))]); 261 io2Append.append(lPadding); 262 io2Append.append(lPadding); 263 } 264 io2Append.append('\n'); 265 return io2Append; 266 } 267 268 276 private static StringBuffer qpencode(StringBuffer io2Append, String i2Encode, int iLength) { 277 io2Append.ensureCapacity(1024); 278 int lCurLineLength = 0; 279 int lPrevChar = -1; 280 char[] l2Encode = i2Encode.toCharArray(); 281 try { 282 for (int i = 0;; i++) { 283 char lCurChar = l2Encode[i]; 284 if (lCurChar > 126 || (lCurChar < 32 && lCurChar != '\n' && lCurChar != '\t') || lCurChar == '=') { 285 io2Append.append('='); 286 io2Append.append((char)hex_table[lCurChar >> 4]); 287 io2Append.append((char)hex_table[lCurChar & 0x0f]); 288 lCurLineLength += 3; 289 lPrevChar = -1; 290 } else if (lCurChar == '\n') { 291 if (lPrevChar == ' ' || lPrevChar == '\t') { 292 io2Append.append('='); 293 io2Append.append(lCurChar); 294 } 295 io2Append.append(lCurChar); 296 lCurLineLength = 0; 297 lPrevChar = lCurChar; 298 } else { 299 io2Append.append(lCurChar); 300 lCurLineLength++; 301 lPrevChar = lCurChar; 302 } 303 if (lCurLineLength > iLength) { 304 io2Append.append('='); 305 io2Append.append('\n'); 306 lCurLineLength = 0; 307 lPrevChar = '\n'; 308 } 309 } 310 } catch (ArrayIndexOutOfBoundsException e) { 311 } 314 315 if (lCurLineLength > 0) { 316 io2Append.append('='); 317 io2Append.append('\n'); 318 } 319 return io2Append; 320 } 321 322 private static String convert2String(IRubyObject l2Conv) { 323 Ruby runtime = l2Conv.getRuntime(); 324 if (l2Conv.getMetaClass() != runtime.getString()) { 325 l2Conv = l2Conv.convertToType("String", "to_s", true); } 327 return ((RubyString) l2Conv).toString(); 328 } 329 330 565 public static RubyArray unpack(Ruby runtime, ByteList encodedString, 566 ByteList formatString) { 567 RubyArray result = runtime.newArray(); 568 ByteBuffer format = ByteBuffer.wrap(formatString.bytes()); 570 ByteBuffer encode = ByteBuffer.wrap(encodedString.bytes()); 571 int type = 0; 572 int next = safeGet(format); 573 574 while (next != 0) { 575 type = next; 576 next = safeGet(format); 577 if (next == '_' || next == '!') { 579 if (NATIVE_CODES.indexOf(type) == -1) { 580 throw runtime.newArgumentError("'" + next + 581 "' allowed only after types " + NATIVE_CODES); 582 } 583 next = safeGet(format); 584 } 585 586 int occurrences = 0; 588 if (next == 0) { 589 occurrences = 1; 590 } else { 591 if (next == '*') { 592 occurrences = IS_STAR; 593 next = safeGet(format); 594 } else if (Character.isDigit((char)(next & 0xFF))) { 595 occurrences = 0; 596 do { 597 occurrences = occurrences * 10 + Character.digit((char)(next & 0xFF), 10); 598 next = safeGet(format); 599 } while (next != 0 && Character.isDigit((char)(next & 0xFF))); 600 } else { 601 occurrences = type == '@' ? 0 : 1; 602 } 603 } 604 605 Converter converter = converters[type]; 607 if (converter != null) { 608 decode(runtime, encode, occurrences, result, converter); 609 type = next; 610 continue; 611 } 612 613 switch (type) { 615 case '@' : 616 encode.position(occurrences); 617 break; 618 case '%' : 619 throw runtime.newArgumentError("% is not supported"); 620 case 'A' : 621 { 622 if (occurrences == IS_STAR || occurrences > encode.remaining()) { 623 occurrences = encode.remaining(); 624 } 625 626 byte[] potential = new byte[occurrences]; 627 encode.get(potential); 628 629 for (int t = occurrences - 1; occurrences > 0; occurrences--, t--) { 630 byte c = potential[t]; 631 632 if (c != '\0' && c != ' ') { 633 break; 634 } 635 } 636 637 result.append(RubyString.newString(runtime, new ByteList(potential, 0, occurrences,false))); 638 } 639 break; 640 case 'Z' : 641 { 642 if (occurrences == IS_STAR || occurrences > encode.remaining()) { 643 occurrences = encode.remaining(); 644 } 645 646 byte[] potential = new byte[occurrences]; 647 encode.get(potential); 648 649 for (int t = occurrences - 1; occurrences > 0; occurrences--, t--) { 650 char c = (char)potential[t]; 651 652 if (c != '\0') { 653 break; 654 } 655 } 656 657 result.append(RubyString.newString(runtime, new ByteList(potential, 0, occurrences,false))); 658 } 659 break; 660 case 'a' : 661 if (occurrences == IS_STAR || occurrences > encode.remaining()) { 662 occurrences = encode.remaining(); 663 } 664 byte[] potential = new byte[occurrences]; 665 encode.get(potential); 666 result.append(RubyString.newString(runtime, new ByteList(potential,false))); 667 break; 668 case 'b' : 669 { 670 if (occurrences == IS_STAR || occurrences > encode.remaining() * 8) { 671 occurrences = encode.remaining() * 8; 672 } 673 int bits = 0; 674 byte[] lElem = new byte[occurrences]; 675 for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) { 676 if ((lCurByte & 7) != 0) { 677 bits >>>= 1; 678 } else { 679 bits = encode.get(); 680 } 681 lElem[lCurByte] = (bits & 1) != 0 ? (byte)'1' : (byte)'0'; 682 } 683 result.append(RubyString.newString(runtime, new ByteList(lElem,false))); 684 } 685 break; 686 case 'B' : 687 { 688 if (occurrences == IS_STAR || occurrences > encode.remaining() * 8) { 689 occurrences = encode.remaining() * 8; 690 } 691 int bits = 0; 692 byte[] lElem = new byte[occurrences]; 693 for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) { 694 if ((lCurByte & 7) != 0) 695 bits <<= 1; 696 else 697 bits = encode.get(); 698 lElem[lCurByte] = (bits & 128) != 0 ? (byte)'1' : (byte)'0'; 699 } 700 701 result.append(RubyString.newString(runtime, new ByteList(lElem,false))); 702 } 703 break; 704 case 'h' : 705 { 706 if (occurrences == IS_STAR || occurrences > encode.remaining() * 2) { 707 occurrences = encode.remaining() * 2; 708 } 709 int bits = 0; 710 byte[] lElem = new byte[occurrences]; 711 for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) { 712 if ((lCurByte & 1) != 0) { 713 bits >>>= 4; 714 } else { 715 bits = encode.get(); 716 } 717 lElem[lCurByte] = sHexDigits[bits & 15]; 718 } 719 result.append(RubyString.newString(runtime, new ByteList(lElem,false))); 720 } 721 break; 722 case 'H' : 723 { 724 if (occurrences == IS_STAR || occurrences > encode.remaining() * 2) { 725 occurrences = encode.remaining() * 2; 726 } 727 int bits = 0; 728 byte[] lElem = new byte[occurrences]; 729 for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) { 730 if ((lCurByte & 1) != 0) 731 bits <<= 4; 732 else 733 bits = encode.get(); 734 lElem[lCurByte] = sHexDigits[(bits >>> 4) & 15]; 735 } 736 result.append(RubyString.newString(runtime, new ByteList(lElem,false))); 737 } 738 break; 739 740 case 'u': 741 { 742 int length = encode.remaining() * 3 / 4; 743 byte[] lElem = new byte[length]; 744 int index = 0; 745 int s; 746 int total = 0; 747 s = encode.get(); 748 while (encode.hasRemaining() && s > ' ' && s < 'a') { 749 int a, b, c, d; 750 byte[] hunk = new byte[3]; 751 752 int len = (s - ' ') & 077; 753 s = safeGet(encode); 754 total += len; 755 if (total > length) { 756 len -= total - length; 757 total = length; 758 } 759 760 while (len > 0) { 761 int mlen = len > 3 ? 3 : len; 762 763 if (encode.hasRemaining() && s >= ' ') { 764 a = (s - ' ') & 077; 765 s = safeGet(encode); 766 } else 767 a = 0; 768 if (encode.hasRemaining() && s >= ' ') { 769 b = (s - ' ') & 077; 770 s = safeGet(encode); 771 } else 772 b = 0; 773 if (encode.hasRemaining() && s >= ' ') { 774 c = (s - ' ') & 077; 775 s = safeGet(encode); 776 } else 777 c = 0; 778 if (encode.hasRemaining() && s >= ' ') { 779 d = (s - ' ') & 077; 780 s = safeGet(encode); 781 } else 782 d = 0; 783 hunk[0] = (byte)((a << 2 | b >> 4) & 255); 784 hunk[1] = (byte)((b << 4 | c >> 2) & 255); 785 hunk[2] = (byte)((c << 6 | d) & 255); 786 787 for (int i = 0; i < mlen; i++) lElem[index++] = hunk[i]; 788 len -= mlen; 789 } 790 if (s == '\r') 791 s = safeGet(encode); 792 if (s == '\n') 793 s = safeGet(encode); 794 else if (encode.hasRemaining()) { 795 if (safeGet(encode) == '\n') { 796 safeGet(encode); } else if (encode.hasRemaining()) { 798 encode.position(encode.position() - 1); 799 } 800 } 801 } 802 result.append(RubyString.newString(runtime, new ByteList(lElem, 0, index,false))); 803 } 804 break; 805 806 case 'm': 807 { 808 int length = encode.remaining()*3/4; 809 byte[] lElem = new byte[length]; 810 int a = -1, b = -1, c = 0, d; 811 int index = 0; 812 while (encode.hasRemaining()) { 813 int s; 814 do { 815 s = safeGet(encode); 816 } while (s == '\r' || s == '\n'); 817 818 if ((a = b64_xtable[s]) == -1) break; 819 s = safeGet(encode); 820 if ((b = b64_xtable[s]) == -1) break; 821 s = safeGet(encode); 822 if ((c = b64_xtable[s]) == -1) break; 823 s = safeGet(encode); 824 if ((d = b64_xtable[s]) == -1) break; 825 826 lElem[index++] = (byte)((a << 2 | b >> 4) & 255); 827 lElem[index++] = (byte)((b << 4 | c >> 2) & 255); 828 lElem[index++] = (byte)((c << 6 | d) & 255); 829 a = -1; 830 } 831 if (a != -1 && b != -1) { 832 lElem[index++] = (byte)((a << 2 | b >> 4) & 255); 833 if(c != -1) { 834 lElem[index++] = (byte)((b << 4 | c >> 2) & 255); 835 } 836 837 } 838 result.append(RubyString.newString(runtime, new ByteList(lElem, 0, index,false))); 839 } 840 break; 841 842 case 'M' : 843 { 844 byte[] lElem = new byte[Math.max(encode.remaining(),0)]; 845 int index = 0; 846 for(;;) { 847 byte c = safeGet(encode); 848 if (!encode.hasRemaining()) break; 849 if (c != '=') { 850 lElem[index++] = c; 851 } else { 852 byte c1 = safeGet(encode); 853 if (!encode.hasRemaining()) break; 854 if (c1 == '\n') continue; 855 byte c2 = safeGet(encode); 856 if (!encode.hasRemaining()) break; 857 byte value = (byte)(Character.digit((char)(c1 & 0xFF), 16) * 16 + Character.digit((char)(c2 & 0xFF), 16)); 858 lElem[index++] = value; 859 } 860 } 861 result.append(RubyString.newString(runtime, new ByteList(lElem, 0, index,false))); 862 } 863 break; 864 case 'U' : 865 { 866 if (occurrences == IS_STAR || occurrences > encode.remaining()) { 867 occurrences = encode.remaining(); 868 } 869 byte[] toUnpack = new byte[occurrences]; 871 encode.get(toUnpack); 872 CharBuffer lUtf8 = null; 873 try { 874 Charset utf8 = Charset.forName("UTF-8"); 875 CharsetDecoder utf8Decoder = utf8.newDecoder(); 876 utf8Decoder.onMalformedInput(CodingErrorAction.REPORT); 877 utf8Decoder.onUnmappableCharacter(CodingErrorAction.REPORT); 878 ByteBuffer buffer = ByteBuffer.wrap(toUnpack); 879 880 lUtf8 = utf8Decoder.decode(buffer); 881 } catch (CharacterCodingException cce) { 882 throw runtime.newArgumentError("malformed UTF-8 character"); 884 } 885 while (occurrences-- > 0 && lUtf8.hasRemaining()) { 886 long lCurChar = lUtf8.get(); 887 result.append(runtime.newFixnum(lCurChar)); 888 } 889 } 890 break; 891 case 'X': 892 if (occurrences == IS_STAR) { 893 occurrences = encode.limit() - encode.remaining(); 894 } 895 896 try { 897 encode.position(encode.position() - occurrences); 898 } catch (IllegalArgumentException e) { 899 throw runtime.newArgumentError("in `unpack': X outside of string"); 900 } 901 break; 902 case 'x': 903 if (occurrences == IS_STAR) { 904 occurrences = encode.remaining(); 905 } 906 907 try { 908 encode.position(encode.position() + occurrences); 909 } catch (IllegalArgumentException e) { 910 throw runtime.newArgumentError("in `unpack': x outside of string"); 911 } 912 913 break; 914 } 915 } 916 return result; 917 } 918 919 private static byte safeGet(ByteBuffer encode) { 920 return encode.hasRemaining() ? encode.get() : 0; 921 } 922 923 public static void decode(Ruby runtime, ByteBuffer encode, int occurrences, 924 RubyArray result, Converter converter) { 925 int lPadLength = 0; 926 927 if (occurrences == IS_STAR) { 928 occurrences = encode.remaining() / converter.size; 929 } else if (occurrences > encode.remaining() / converter.size) { 930 lPadLength = occurrences - encode.remaining() / converter.size; 931 occurrences = encode.remaining() / converter.size; 932 } 933 for (; occurrences-- > 0;) { 934 result.append(converter.decode(runtime, encode)); 935 } 936 for (; lPadLength-- > 0;) 937 result.append(runtime.getNil()); 938 } 939 940 public static int encode(Ruby runtime, int occurrences, StringBuffer result, 941 List list, int index, Converter converter) { 942 int listSize = list.size(); 943 944 while (occurrences-- > 0) { 945 if (listSize-- <= 0) { 946 throw runtime.newArgumentError(sTooFew); 947 } 948 949 IRubyObject from = (IRubyObject) list.get(index++); 950 951 converter.encode(runtime, from, result); 952 } 953 954 return index; 955 } 956 957 public abstract static class Converter { 958 public int size; 959 960 public Converter(int size) { 961 this.size = size; 962 } 963 964 public abstract IRubyObject decode(Ruby runtime, ByteBuffer format); 965 public abstract void encode(Ruby runtime, IRubyObject from, 966 StringBuffer result); 967 } 968 969 static class PtrList { 970 private byte[] buffer; private int index; 973 public PtrList(byte[] bufferString) { 974 buffer = bufferString; 975 index = 0; 976 } 977 978 981 public int remaining() { 982 return buffer.length - index; 983 } 984 985 992 public String nextSubstring(int length) { 993 if (index + length > buffer.length) { 995 throw new IllegalArgumentException (); 996 } 997 998 String substring = null; 999 substring = new String (ByteList.plain(buffer), index, length); 1000 1001 index += length; 1002 1003 return substring; 1004 } 1005 1006 public void setPosition(int position) { 1007 if (position < buffer.length) { 1008 index = position; 1009 } 1010 } 1011 1012 1015 public int nextAsciiNumber() { 1016 int i = index; 1017 1018 for (; i < buffer.length; i++) { 1019 if (!Character.isDigit((char)(buffer[i] & 0xFF))) { 1020 break; 1021 } 1022 } 1023 1024 int number = 0; 1026 Integer.parseInt(new String (ByteList.plain(buffer), index, i - index)); 1027 1028 index = i; 1030 return number; 1031 } 1032 1033 1036 public int getLength() { 1037 return buffer.length; 1038 } 1039 1040 1046 public int nextByte() { 1047 byte next = 0; 1048 1049 if (index < buffer.length) { 1050 next = buffer[index++]; 1051 } else if (index == buffer.length) { 1052 index++; 1053 } 1054 1055 return next & 0xFF; 1056 } 1057 1058 1064 public void backup(int occurrences) { 1065 index -= occurrences; 1066 1067 if (index < 0) { 1068 throw new IllegalArgumentException (); 1069 } 1070 } 1071 1072 1075 public boolean isAtEnd() { 1076 return index > buffer.length; 1077 } 1078 1079 1082 public int getIndex() { 1083 return index; 1084 } 1085 } 1086 1087 1094 private static final StringBuffer shrink(StringBuffer i2Shrink, int iLength) { 1095 iLength = i2Shrink.length() - iLength; 1096 1097 if (iLength < 0) { 1098 throw new IllegalArgumentException (); 1099 } 1100 i2Shrink.setLength(iLength); 1101 return i2Shrink; 1102 } 1103 1104 1112 private static final StringBuffer grow(StringBuffer i2Grow, String iPads, int iLength) { 1113 int lPadLength = iPads.length(); 1114 while (iLength >= lPadLength) { 1115 i2Grow.append(iPads); 1116 iLength -= lPadLength; 1117 } 1118 i2Grow.append(iPads.substring(0, iLength)); 1119 return i2Grow; 1120 } 1121 1122 1290 public static RubyString pack(Ruby runtime, List list, byte[] formatString) { 1291 ByteBuffer format = ByteBuffer.wrap((byte[])formatString); 1292 StringBuffer result = new StringBuffer (); 1293 int listSize = list.size(); 1294 int type = 0; 1295 int next = safeGet(format); 1296 1297 int idx = 0; 1298 String lCurElemString; 1299 1300 mainLoop: while (next != 0) { 1301 type = next; 1302 next = safeGet(format); 1303 while (Character.isWhitespace((char)(type&0xFF))) { if (next == 0) break mainLoop; 1305 type = next; 1306 next = safeGet(format); 1307 } 1308 1309 if (next == '!' || next == '_') { 1310 if (NATIVE_CODES.indexOf(type) == -1) { 1311 throw runtime.newArgumentError("'" + next + 1312 "' allowed only after types " + NATIVE_CODES); 1313 } 1314 1315 next = safeGet(format); 1316 } 1317 1318 int occurrences = 1; 1320 boolean isStar = false; 1321 if (next != 0) { 1322 if (next == '*') { 1323 if ("@Xxu".indexOf(type) != -1) { 1324 occurrences = 0; 1325 } else { 1326 occurrences = listSize; 1327 isStar = true; 1328 } 1329 next = safeGet(format); 1330 } else if (Character.isDigit((char)(next & 0xFF))) { 1331 occurrences = 0; 1332 do { 1333 occurrences = occurrences * 10 + Character.digit((char)(next & 0xFF), 10); 1334 next = safeGet(format); 1335 } while (next != 0 && Character.isDigit((char)(next & 0xFF))); 1336 } 1337 } 1338 1339 Converter converter = converters[type]; 1340 1341 if (converter != null) { 1342 idx = encode(runtime, occurrences, result, list, idx, converter); 1343 continue; 1344 } 1345 1346 switch (type) { 1347 case '%' : 1348 throw runtime.newArgumentError("% is not supported"); 1349 case 'A' : 1350 case 'a' : 1351 case 'Z' : 1352 case 'B' : 1353 case 'b' : 1354 case 'H' : 1355 case 'h' : 1356 { 1357 if (listSize-- <= 0) { 1358 throw runtime.newArgumentError(sTooFew); 1359 } 1360 1361 IRubyObject from = (IRubyObject) list.get(idx++); 1362 lCurElemString = from == runtime.getNil() ? "" : from.convertToString().toString(); 1363 1364 if (isStar) { 1365 occurrences = lCurElemString.length(); 1366 } 1367 1368 switch (type) { 1369 case 'a' : 1370 case 'A' : 1371 case 'Z' : 1372 if (lCurElemString.length() >= occurrences) { 1373 result.append(lCurElemString.toCharArray(), 0, occurrences); 1374 } else { result.append(lCurElemString); 1378 occurrences -= lCurElemString.length(); 1379 grow(result, (type == 'a') ? sNil10 : sSp10, occurrences); 1380 } 1381 break; 1382 1383 case 'b' : 1385 { 1386 int currentByte = 0; 1387 int padLength = 0; 1388 1389 if (occurrences > lCurElemString.length()) { 1390 padLength = occurrences - lCurElemString.length(); 1391 occurrences = lCurElemString.length(); 1392 } 1393 1394 for (int i = 0; i < occurrences;) { 1395 if ((lCurElemString.charAt(i++) & 1) != 0) { currentByte |= 128; } 1398 1399 if ((i & 7) == 0) { 1400 result.append((char) (currentByte & 0xff)); 1401 currentByte = 0; 1402 continue; 1403 } 1404 1405 currentByte >>= 1; } 1408 1409 if ((occurrences & 7) != 0) { currentByte >>= 7 - (occurrences & 7); result.append((char) (currentByte & 0xff)); 1412 } 1413 1414 result.setLength(result.length() + padLength); 1416 } 1417 break; 1418 case 'B' : 1419 { 1420 int currentByte = 0; 1421 int padLength = 0; 1422 1423 if (occurrences > lCurElemString.length()) { 1424 padLength = occurrences - lCurElemString.length(); 1425 occurrences = lCurElemString.length(); 1426 } 1427 1428 for (int i = 0; i < occurrences;) { 1429 currentByte |= lCurElemString.charAt(i++) & 1; 1430 1431 if ((i & 7) == 0) { 1433 result.append((char) (currentByte & 0xff)); 1434 currentByte = 0; 1435 continue; 1436 } 1437 1438 currentByte <<= 1; 1440 } 1441 1442 if ((occurrences & 7) != 0) { currentByte <<= 7 - (occurrences & 7); result.append((char) (currentByte & 0xff)); 1445 } 1446 1447 result.setLength(result.length() + padLength); 1448 } 1449 break; 1450 case 'h' : 1451 { 1452 int currentByte = 0; 1453 int padLength = 0; 1454 1455 if (occurrences > lCurElemString.length()) { 1456 padLength = occurrences - lCurElemString.length(); 1457 occurrences = lCurElemString.length(); 1458 } 1459 1460 for (int i = 0; i < occurrences;) { 1461 char currentChar = lCurElemString.charAt(i++); 1462 1463 if (Character.isJavaIdentifierStart(currentChar)) { 1464 currentByte |= (((currentChar & 15) + 9) & 15) << 4; 1466 } else { 1467 currentByte |= (currentChar & 15) << 4; 1468 } 1469 1470 if ((i & 1) != 0) { 1471 currentByte >>= 4; 1472 } else { 1473 result.append((char) (currentByte & 0xff)); 1474 currentByte = 0; 1475 } 1476 } 1477 1478 if ((occurrences & 1) != 0) { 1479 result.append((char) (currentByte & 0xff)); 1480 } 1481 1482 result.setLength(result.length() + padLength); 1483 } 1484 break; 1485 case 'H' : 1486 { 1487 int currentByte = 0; 1488 int padLength = 0; 1489 1490 if (occurrences > lCurElemString.length()) { 1491 padLength = occurrences - lCurElemString.length(); 1492 occurrences = lCurElemString.length(); 1493 } 1494 1495 for (int i = 0; i < occurrences;) { 1496 char currentChar = lCurElemString.charAt(i++); 1497 1498 if (Character.isJavaIdentifierStart(currentChar)) { 1499 currentByte |= ((currentChar & 15) + 9) & 15; 1501 } else { 1502 currentByte |= currentChar & 15; 1503 } 1504 1505 if ((i & 1) != 0) { 1506 currentByte <<= 4; 1507 } else { 1508 result.append((char) (currentByte & 0xff)); 1509 currentByte = 0; 1510 } 1511 } 1512 1513 if ((occurrences & 1) != 0) { 1514 result.append((char) (currentByte & 0xff)); 1515 } 1516 1517 result.setLength(result.length() + padLength); 1518 } 1519 break; 1520 } 1521 break; 1522 } 1523 1524 case 'x' : 1525 grow(result, sNil10, occurrences); 1526 break; 1527 case 'X' : 1528 try { 1529 shrink(result, occurrences); 1530 } catch (IllegalArgumentException e) { 1531 throw runtime.newArgumentError("in `pack': X outside of string"); 1532 } 1533 break; 1534 case '@' : 1535 occurrences -= result.length(); 1536 if (occurrences > 0) { 1537 grow(result, sNil10, occurrences); 1538 } 1539 occurrences = -occurrences; 1540 if (occurrences > 0) { 1541 shrink(result, occurrences); 1542 } 1543 break; 1544 case 'u' : 1545 case 'm' : 1546 { 1547 if (listSize-- <= 0) { 1548 throw runtime.newArgumentError(sTooFew); 1549 } 1550 IRubyObject from = (IRubyObject) list.get(idx++); 1551 lCurElemString = from == runtime.getNil() ? "" : from.convertToString().toString(); 1552 occurrences = occurrences <= 2 ? 45 : occurrences / 3 * 3; 1553 1554 for (;;) { 1555 encodes(runtime, result, lCurElemString, occurrences, (char)type); 1556 1557 if (occurrences >= lCurElemString.length()) { 1558 break; 1559 } 1560 1561 lCurElemString = lCurElemString.substring(occurrences); 1562 } 1563 } 1564 break; 1565 case 'M' : 1566 { 1567 if (listSize-- <= 0) { 1568 throw runtime.newArgumentError(sTooFew); 1569 } 1570 1571 IRubyObject from = (IRubyObject) list.get(idx++); 1572 lCurElemString = from == runtime.getNil() ? "" : from.convertToString().toString(); 1573 1574 if (occurrences <= 1) { 1575 occurrences = 72; 1576 } 1577 1578 qpencode(result, lCurElemString, occurrences); 1579 } 1580 break; 1581 case 'U' : 1582 1583 char[] c = new char[occurrences]; 1584 for (int cIndex = 0; occurrences-- > 0; cIndex++) { 1585 if (listSize-- <= 0) { 1586 throw runtime.newArgumentError(sTooFew); 1587 } 1588 1589 IRubyObject from = (IRubyObject) list.get(idx++); 1590 long l = from == runtime.getNil() ? 0 : RubyNumeric.num2long(from); 1591 1592 c[cIndex] = (char) l; 1593 } 1594 1595 try { 1596 byte[] bytes = new String (c).getBytes("UTF8"); 1597 result.append(RubyString.bytesToString(bytes)); 1598 } catch (java.io.UnsupportedEncodingException e) { 1599 assert false : "can't convert to UTF8"; 1600 } 1601 break; 1602 } 1603 } 1604 return runtime.newString(result.toString()); 1605 } 1606 1607 1614 private static int decodeIntLittleEndian(ByteBuffer encode) { 1615 encode.order(ByteOrder.LITTLE_ENDIAN); 1616 int value = encode.getInt(); 1617 encode.order(ByteOrder.BIG_ENDIAN); 1618 return value; 1619 } 1620 1621 1628 private static int decodeIntBigEndian(ByteBuffer encode) { 1629 return encode.getInt(); 1630 } 1631 1632 1639 private static long decodeIntUnsignedBigEndian(ByteBuffer encode) { 1640 return (long)encode.getInt() & 0xFFFFFFFFL; 1641 } 1642 1643 1650 private static long decodeIntUnsignedLittleEndian(ByteBuffer encode) { 1651 encode.order(ByteOrder.LITTLE_ENDIAN); 1652 long value = encode.getInt() & 0xFFFFFFFFL; 1653 encode.order(ByteOrder.BIG_ENDIAN); 1654 return value; 1655 } 1656 1657 1663 private static void encodeIntLittleEndian(StringBuffer result, int s) { 1664 result.append((char) (s & 0xff)).append((char) ((s >> 8) & 0xff)); 1665 result.append((char) ((s>>16) & 0xff)).append((char) ((s>>24) &0xff)); 1666 } 1667 1668 1674 private static void encodeIntBigEndian(StringBuffer result, int s) { 1675 result.append((char) ((s>>24) &0xff)).append((char) ((s>>16) &0xff)); 1676 result.append((char) ((s >> 8) & 0xff)).append((char) (s & 0xff)); 1677 } 1678 1679 1685 private static long decodeLongBigEndian(ByteBuffer encode) { 1686 int c1 = decodeIntBigEndian(encode); 1687 int c2 = decodeIntBigEndian(encode); 1688 1689 return ((long) c1 << 32) + (c2 & 0xffffffffL); 1690 } 1691 1692 1698 private static long decodeLongLittleEndian(ByteBuffer encode) { 1699 int c1 = decodeIntLittleEndian(encode); 1700 int c2 = decodeIntLittleEndian(encode); 1701 1702 return ((long) c2 << 32) + (c1 & 0xffffffffL); 1703 } 1704 1705 1711 private static void encodeLongLittleEndian(StringBuffer result, long l) { 1712 encodeIntLittleEndian(result, (int) (l & 0xffffffff)); 1713 encodeIntLittleEndian(result, (int) (l >>> 32)); 1714 } 1715 1716 1722 private static void encodeLongBigEndian(StringBuffer result, long l) { 1723 encodeIntBigEndian(result, (int) (l >>> 32)); 1724 encodeIntBigEndian(result, (int) (l & 0xffffffff)); 1725 } 1726 1727 1733 private static double decodeDoubleLittleEndian(ByteBuffer encode) { 1734 return Double.longBitsToDouble(decodeLongLittleEndian(encode)); 1735 } 1736 1737 1743 private static double decodeDoubleBigEndian(ByteBuffer encode) { 1744 return Double.longBitsToDouble(decodeLongBigEndian(encode)); 1745 } 1746 1747 1753 private static void encodeDoubleLittleEndian(StringBuffer result, double d) { 1754 encodeLongLittleEndian(result, Double.doubleToLongBits(d)); 1755 } 1756 1757 1763 private static void encodeDoubleBigEndian(StringBuffer result, double d) { 1764 encodeLongBigEndian(result, Double.doubleToLongBits(d)); 1765 } 1766 1767 1773 private static float decodeFloatBigEndian(ByteBuffer encode) { 1774 return Float.intBitsToFloat(decodeIntBigEndian(encode)); 1775 } 1776 1777 1783 private static float decodeFloatLittleEndian(ByteBuffer encode) { 1784 return Float.intBitsToFloat(decodeIntLittleEndian(encode)); 1785 } 1786 1787 1792 private static void encodeFloatLittleEndian(StringBuffer result, float f) { 1793 encodeIntLittleEndian(result, Float.floatToIntBits(f)); 1794 } 1795 1796 1801 private static void encodeFloatBigEndian(StringBuffer result, float f) { 1802 encodeIntBigEndian(result, Float.floatToIntBits(f)); 1803 } 1804 1805 1811 private static int decodeShortUnsignedLittleEndian(ByteBuffer encode) { 1812 encode.order(ByteOrder.LITTLE_ENDIAN); 1813 int value = encode.getShort() & 0xFFFF; 1814 encode.order(ByteOrder.BIG_ENDIAN); 1815 return value; 1816 } 1817 1818 1824 private static int decodeShortUnsignedBigEndian(ByteBuffer encode) { 1825 int value = encode.getShort() & 0xFFFF; 1826 return value; 1827 } 1828 1829 1835 private static short decodeShortBigEndian(ByteBuffer encode) { 1836 return encode.getShort(); 1837 } 1838 1839 1845 private static void encodeShortLittleEndian(StringBuffer result, int s) { 1846 result.append((char) (s & 0xff)).append((char) ((s & 0xff00) >> 8)); 1847 } 1848 1849 1855 private static void encodeShortBigEndian(StringBuffer result, int s) { 1856 result.append((char) ((s & 0xff00) >> 8)).append((char) (s & 0xff)); 1857 } 1858} 1859 | Popular Tags |