package org.jruby.util;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.*;
import org.jruby.platform.Platform;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import static com.headius.backport9.buffer.Buffers.markBuffer;
import static com.headius.backport9.buffer.Buffers.positionBuffer;
import static org.jruby.util.TypeConverter.toFloat;
public class Pack {
private static final byte[] sSp10 = " ".getBytes();
private static final byte[] sNil10 = "\000\000\000\000\000\000\000\000\000\000".getBytes();
private static final int IS_STAR = -1;
private static final ASCIIEncoding ASCII = ASCIIEncoding.INSTANCE;
private static final USASCIIEncoding USASCII = USASCIIEncoding.INSTANCE;
private static final UTF8Encoding UTF8 = UTF8Encoding.INSTANCE;
private static final String NATIVE_CODES = "sSiIlL";
private static final String MAPPED_CODES = "sSiIqQ";
private static final char BE = '>' - 1;
private static final char LE = '<';
private static final String ENDIANESS_CODES = new String(new char[] {
's' + BE, 'S' + BE, 'i' + BE, 'I' + BE, 'l' + BE, 'L' + BE, 'q' + BE, 'Q' + BE,
's' + LE, 'S' + LE, 'i' + LE, 'I' + LE, 'l' + LE, 'L' + LE, 'q' + LE, 'Q' + LE});
private static final String UNPACK_IGNORE_NULL_CODES = "cC";
private static final String PACK_IGNORE_NULL_CODES = "cCiIlLnNqQsSvV";
private static final String PACK_IGNORE_NULL_CODES_WITH_MODIFIERS = "lLsS";
private static final int UNPACK_ARRAY = 0;
private static final int UNPACK_BLOCK = 1;
private static final int UNPACK_1 = 2;
private static final String sTooFew = "too few arguments";
private static final byte[] uu_table;
private static final byte[] b64_table;
public static final byte[] sHexDigits;
public static final int[] b64_xtable = new int[256];
private static final Converter[] converters = new Converter[256];
private static long num2quad(IRubyObject arg) {
if (arg.isNil()) return 0L;
if (arg instanceof RubyBignum) {
BigInteger big = ((RubyBignum) arg).getValue();
return big.longValue();
}
return RubyNumeric.num2long(arg);
}
private static float obj2flt(Ruby runtime, IRubyObject o) {
return (float) toFloat(runtime, o).getDoubleValue();
}
private static double obj2dbl(Ruby runtime, IRubyObject o) {
return toFloat(runtime, o).getDoubleValue();
}
static {
uu_table =
ByteList.plain("`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_");
b64_table =
ByteList.plain("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
sHexDigits = ByteList.plain("0123456789abcdef0123456789ABCDEFx");
for (int i = 0; i < 256; i++) {
b64_xtable[i] = -1;
}
for (int i = 0; i < 64; i++) {
b64_xtable[(int)b64_table[i]] = i;
}
converters['e'] = new Converter(4) {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return RubyFloat.newFloat(runtime, decodeFloatLittleEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeFloatLittleEndian(result, obj2flt(runtime, o));
}
};
converters['g'] = new Converter(4) {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return RubyFloat.newFloat(runtime, decodeFloatBigEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeFloatBigEndian(result, obj2flt(runtime, o));
}
};
Converter tmp = new Converter(4) {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return RubyFloat.newFloat(runtime,
Platform.BYTE_ORDER == Platform.BIG_ENDIAN ?
decodeFloatBigEndian(enc) : decodeFloatLittleEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result) {
if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
encodeFloatBigEndian(result, obj2flt(runtime, o));
} else {
encodeFloatLittleEndian(result, obj2flt(runtime, o));
}
}
};
converters['F'] = tmp;
converters['f'] = tmp;
converters['E'] = new Converter(8) {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return RubyFloat.newFloat(runtime, decodeDoubleLittleEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeDoubleLittleEndian(result, obj2dbl(runtime, o));
}
};
converters['G'] = new Converter(8) {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return RubyFloat.newFloat(runtime, decodeDoubleBigEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeDoubleBigEndian(result, obj2dbl(runtime, o));
}
};
tmp = new Converter(8) {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
return RubyFloat.newFloat(runtime, decodeDoubleBigEndian(enc));
} else {
return RubyFloat.newFloat(runtime, decodeDoubleLittleEndian(enc));
}
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeDoubleLittleEndian(result, obj2dbl(runtime, o));
}
};
converters['D'] = tmp;
converters['d'] = tmp;
tmp = new QuadConverter(2, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(decodeShortUnsignedLittleEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeShortLittleEndian(result, overflowQuad(num2quad(o)));
}
};
converters['v'] = tmp;
converters['S' + LE] = tmp;
tmp = new QuadConverter(2, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(decodeShortUnsignedBigEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result) {
encodeShortBigEndian(result, overflowQuad(num2quad(o)));
}
};
converters['n'] = tmp;
converters['S' + BE] = tmp;
converters['s'] = new QuadConverter(2, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(Platform.BYTE_ORDER == Platform.BIG_ENDIAN ?
decodeShortBigEndian(enc) : decodeShortLittleEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result) {
encodeShortByByteOrder(result, overflowQuad(num2quad(o)));
}
};
converters['S'] = new QuadConverter(2, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(Platform.BYTE_ORDER == Platform.BIG_ENDIAN ?
decodeShortUnsignedBigEndian(enc) : decodeShortUnsignedLittleEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeShortByByteOrder(result, overflowQuad(num2quad(o)));
}
};
converters['s' + LE] = new QuadConverter(2, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(decodeShortLittleEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result) {
encodeShortLittleEndian(result, overflowQuad(num2quad(o)));
}
};
converters['s' + BE] = new QuadConverter(2, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(decodeShortBigEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result) {
encodeShortBigEndian(result, overflowQuad(num2quad(o)));
}
};
converters['c'] = new Converter(1, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
int c = enc.get();
return runtime.newFixnum(c > (char) 127 ? c-256 : c);
}
public void encode(Ruby runtime, IRubyObject o, ByteList result) {
byte c = (byte) (num2quad(o) & 0xff);
result.append(c);
}
};
converters['C'] = new Converter(1, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(enc.get() & 0xFF);
}
public void encode(Ruby runtime, IRubyObject o, ByteList result){
byte c = o == runtime.getNil() ? 0 : (byte) (num2quad(o) & 0xff);
result.append(c);
}
};
tmp = new Converter(4, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(decodeIntUnsignedLittleEndian(enc));
}
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeIntLittleEndian(result, (int) RubyNumeric.num2long(o));
}
};
converters['V'] = tmp;
converters['L' + LE] = tmp;
converters['I' + LE] = tmp;
tmp = new Converter(4, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(decodeIntUnsignedBigEndian(enc));
}
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeIntBigEndian(result, (int) RubyNumeric.num2long(o));
}
};
converters['N'] = tmp;
converters['L' + BE] = tmp;
converters['I' + BE] = tmp;
tmp = new Converter(4, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
return runtime.newFixnum(decodeIntUnsignedBigEndian(enc));
} else {
return runtime.newFixnum(decodeIntUnsignedLittleEndian(enc));
}
}
public void encode(Ruby runtime, IRubyObject o, ByteList result){
int s = o == runtime.getNil() ? 0 : (int) RubyNumeric.num2long(o);
packInt_i(result, s);
}
};
converters['I'] = tmp;
converters['L'] = tmp;
tmp = new Converter(4, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
int value = unpackInt_i(enc);
return runtime.newFixnum(value);
}
public void encode(Ruby runtime, IRubyObject o, ByteList result){
int s = o == runtime.getNil() ? 0 : (int)RubyNumeric.num2long(o);
packInt_i(result, s);
}
};
converters['i'] = tmp;
converters['l'] = tmp;
tmp = new Converter(4, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(decodeIntLittleEndian(enc));
}
public void encode(Ruby runtime, IRubyObject o, ByteList result){
int s = o == runtime.getNil() ? 0 : (int)RubyNumeric.num2long(o);
encodeIntLittleEndian(result, s);
}
};
converters['i' + LE] = tmp;
converters['l' + LE] = tmp;
tmp = new Converter(4, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(decodeIntBigEndian(enc));
}
public void encode(Ruby runtime, IRubyObject o, ByteList result){
int s = o == runtime.getNil() ? 0 : (int)RubyNumeric.num2long(o);
encodeIntBigEndian(result, s);
}
};
converters['i' + BE] = tmp;
converters['l' + BE] = tmp;
converters['Q'] = new QuadConverter(8, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
long l = Platform.BYTE_ORDER == Platform.BIG_ENDIAN ? decodeLongBigEndian(enc) : decodeLongLittleEndian(enc);
return RubyBignum.bignorm(runtime,BigInteger.valueOf(l).and(new BigInteger("FFFFFFFFFFFFFFFF", 16)));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeLongByByteOrder(result, num2quad(o));
}
};
converters['Q' + LE] = new QuadConverter(8, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
long l = decodeLongLittleEndian(enc);
return RubyBignum.bignorm(runtime,BigInteger.valueOf(l).and(new BigInteger("FFFFFFFFFFFFFFFF", 16)));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeLongLittleEndian(result, num2quad(o));
}
};
converters['Q' + BE] = new QuadConverter(8, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
long l = decodeLongBigEndian(enc);
return RubyBignum.bignorm(runtime,BigInteger.valueOf(l).and(new BigInteger("FFFFFFFFFFFFFFFF", 16)));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeLongBigEndian(result, num2quad(o));
}
};
converters['q'] = new QuadConverter(8, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(Platform.BYTE_ORDER == Platform.BIG_ENDIAN ?
decodeLongBigEndian(enc) : decodeLongLittleEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeLongByByteOrder(result, num2quad(o));
}
};
converters['q' + LE] = new QuadConverter(8, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(decodeLongLittleEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeLongLittleEndian(result, num2quad(o));
}
};
converters['q' + BE] = new QuadConverter(8, "Integer") {
public IRubyObject decode(Ruby runtime, ByteBuffer enc) {
return runtime.newFixnum(decodeLongBigEndian(enc));
}
@Override
public void encode(Ruby runtime, IRubyObject o, ByteList result){
encodeLongBigEndian(result, num2quad(o));
}
};
}
public static int unpackInt_i(ByteBuffer enc) {
int value;
if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
value = decodeIntBigEndian(enc);
} else {
value = decodeIntLittleEndian(enc);
}
return value;
}
public static ByteList packInt_i(ByteList result, int s) {
if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
encodeIntBigEndian(result, s);
} else {
encodeIntLittleEndian(result, s);
}
return result;
}
public static void encodeUM(Ruby runtime, ByteList lCurElemString, int occurrences, boolean ignoreStar, char type, ByteList result) {
if (occurrences == 0 && type == 'm' && !ignoreStar) {
encodes(runtime, result, lCurElemString.getUnsafeBytes(),
lCurElemString.getBegin(), lCurElemString.length(),
lCurElemString.length(), (byte) type, false);
return;
}
occurrences = occurrences <= 2 ? 45 : occurrences / 3 * 3;
if (lCurElemString.length() == 0) return;
byte[] charsToEncode = lCurElemString.getUnsafeBytes();
for (int i = 0; i < lCurElemString.length(); i += occurrences) {
encodes(runtime, result, charsToEncode,
i + lCurElemString.getBegin(), lCurElemString.length() - i,
occurrences, (byte)type, true);
}
}
private static ByteList encodes(Ruby runtime, ByteList io2Append,byte[] charsToEncode, int startIndex, int length, int charCount, byte encodingType, boolean tailLf) {
charCount = charCount < length ? charCount : length;
io2Append.ensure(charCount * 4 / 3 + 6);
int i = startIndex;
byte[] lTranslationTable = encodingType == 'u' ? uu_table : b64_table;
byte lPadding;
if (encodingType == 'u') {
if (charCount >= lTranslationTable.length) {
throw runtime.newArgumentError(charCount
+ " is not a correct value for the number of bytes per line in a u directive. Correct values range from 0 to "
+ lTranslationTable.length);
}
io2Append.append(lTranslationTable[charCount]);
lPadding = '`';
} else {
lPadding = '=';
}
while (charCount >= 3) {
byte lCurChar = charsToEncode[i++];
byte lNextChar = charsToEncode[i++];
byte lNextNextChar = charsToEncode[i++];
io2Append.append(lTranslationTable[077 & (lCurChar >>> 2)]);
io2Append.append(lTranslationTable[077 & (((lCurChar << 4) & 060) | ((lNextChar >>> 4) & 017))]);
io2Append.append(lTranslationTable[077 & (((lNextChar << 2) & 074) | ((lNextNextChar >>> 6) & 03))]);
io2Append.append(lTranslationTable[077 & lNextNextChar]);
charCount -= 3;
}
if (charCount == 2) {
byte lCurChar = charsToEncode[i++];
byte lNextChar = charsToEncode[i++];
io2Append.append(lTranslationTable[077 & (lCurChar >>> 2)]);
io2Append.append(lTranslationTable[077 & (((lCurChar << 4) & 060) | ((lNextChar >> 4) & 017))]);
io2Append.append(lTranslationTable[077 & (((lNextChar << 2) & 074) | (('\0' >> 6) & 03))]);
io2Append.append(lPadding);
} else if (charCount == 1) {
byte lCurChar = charsToEncode[i++];
io2Append.append(lTranslationTable[077 & (lCurChar >>> 2)]);
io2Append.append(lTranslationTable[077 & (((lCurChar << 4) & 060) | (('\0' >>> 4) & 017))]);
io2Append.append(lPadding);
io2Append.append(lPadding);
}
if (tailLf) {
io2Append.append('\n');
}
return io2Append;
}
public static RubyArray unpack(Ruby runtime, ByteList encodedString, ByteList formatString) {
return unpackWithBlock(runtime.getCurrentContext(), runtime, encodedString, formatString, Block.NULL_BLOCK);
}
public static RubyArray unpack(ThreadContext context, RubyString encoded, ByteList formatString) {
return unpackWithBlock(context, encoded, formatString, Block.NULL_BLOCK);
}
public static RubyArray unpackWithBlock(ThreadContext context, RubyString encoded, ByteList formatString, Block block) {
return (RubyArray) unpackInternal(context, encoded, formatString, block.isGiven() ? UNPACK_BLOCK : UNPACK_ARRAY, block);
}
public static IRubyObject unpack1WithBlock(ThreadContext context, RubyString encoded, ByteList formatString, Block block) {
return unpackInternal(context, encoded, formatString, UNPACK_1, block);
}
private static IRubyObject unpackInternal(ThreadContext context, RubyString encoded, ByteList formatString, int mode, Block block) {
final Ruby runtime = context.runtime;
final RubyArray result = (mode == UNPACK_BLOCK) || (mode == UNPACK_1) ? null : runtime.newArray();
final ByteList encodedString = encoded.getByteList();
final boolean tainted = encoded.isTaint();
ByteBuffer format = ByteBuffer.wrap(formatString.getUnsafeBytes(), formatString.begin(), formatString.length());
ByteBuffer encode = ByteBuffer.wrap(encodedString.getUnsafeBytes(), encodedString.begin(), encodedString.length());
int next = safeGet(format);
IRubyObject value = null;
mainLoop: while (next != 0) {
int type = next;
next = safeGet(format);
if (UNPACK_IGNORE_NULL_CODES.indexOf(type) != -1 && next == 0) {
next = safeGetIgnoreNull(format);
}
if (type == '#') {
while (type != '\n') {
if (next == 0) break mainLoop;
type = next;
next = safeGet(format);
}
}
if (next == '_' || next == '!') {
int index = NATIVE_CODES.indexOf(type);
if (index == -1) {
throw runtime.newArgumentError("'" + next + "' allowed only after types " + NATIVE_CODES);
}
type = MAPPED_CODES.charAt(index);
next = safeGet(format);
}
if (next == '>' || next == '<') {
next = next == '>' ? BE : LE;
int index = ENDIANESS_CODES.indexOf(type + next);
if (index == -1) {
throw runtime.newArgumentError("'" + (char)next + "' allowed only after types sSiIlLqQ");
}
type = ENDIANESS_CODES.charAt(index);
next = safeGet(format);
if (next == '_' || next == '!') next = safeGet(format);
}
int occurrences;
if (next == 0) {
occurrences = 1;
} else {
if (next == '*') {
occurrences = IS_STAR;
next = safeGet(format);
} else if (ASCII.isDigit(next)) {
occurrences = 0;
do {
occurrences = occurrences * 10 + Character.digit((char)(next & 0xFF), 10);
next = safeGet(format);
} while (next != 0 && ASCII.isDigit(next));
} else {
occurrences = type == '@' ? 0 : 1;
}
}
Converter converter = converters[type];
if (converter != null) {
value = decode(context, runtime, encode, occurrences, result, block, converter, mode);
if (mode == UNPACK_1 && value != null) {
return value;
} else {
continue;
}
}
switch (type) {
case '@':
unpack_at(runtime, encodedString, encode, occurrences);
break;
case '%':
throw runtime.newArgumentError("% is not supported");
case 'A':
value = unpack_A(context, block, result, tainted, encode, occurrences, mode);
break;
case 'Z':
value = unpack_Z(context, block, result, tainted, encode, occurrences, mode);
break;
case 'a':
value = unpack_a(context, block, result, tainted, encode, occurrences, mode);
break;
case 'b':
value = unpack_b(context, block, result, tainted, encode, occurrences, mode);
break;
case 'B':
value = unpack_B(context, block, result, tainted, encode, occurrences, mode);
break;
case 'h':
value = unpack_h(context, block, result, tainted, encode, occurrences, mode);
break;
case 'H':
value = unpack_H(context, block, result, tainted, encode, occurrences, mode);
break;
case 'u':
value = unpack_u(context, block, result, tainted, encode, mode);
break;
case 'm':
value = unpack_m(context, block, runtime, result, tainted, encode, occurrences, mode);
break;
case 'M':
value = unpack_M(context, block, result, tainted, encode, mode);
break;
case 'U':
value = unpack_U(context, block, runtime, result, encode, occurrences, mode);
break;
case 'X':
unpack_X(runtime, encode, occurrences);
break;
case 'x':
unpack_x(runtime, encode, occurrences);
break;
case 'w':
value = unpack_w(context, block, runtime, result, encode, occurrences, mode);
break;
}
if (mode == UNPACK_1 && value != null) {
return value;
}
}
return result;
}
private static IRubyObject unpack_w(ThreadContext context, Block block, Ruby runtime, RubyArray result, ByteBuffer encode, int occurrences, int mode) {
if (occurrences == IS_STAR || occurrences > encode.remaining()) {
occurrences = encode.remaining();
}
long ul = 0;
long ulmask = (0xfeL << 56) & 0xffffffff;
RubyBignum big128 = RubyBignum.newBignum(runtime, 128);
int pos = encode.position();
while (occurrences > 0 && pos < encode.limit()) {
ul <<= 7;
ul |= encode.get(pos) & 0x7f;
if((encode.get(pos++) & 0x80) == 0) {
IRubyObject value = RubyFixnum.newFixnum(runtime, ul);
if (mode == UNPACK_1) {
return value;
}
appendOrYield(context, block, result, value, mode);
occurrences--;
ul = 0;
} else if((ul & ulmask) == 0) {
RubyBignum big = RubyBignum.newBignum(runtime, ul);
while(occurrences > 0 && pos < encode.limit()) {
IRubyObject mulResult = big.op_mul(runtime.getCurrentContext(), big128);
IRubyObject v = mulResult.callMethod(runtime.getCurrentContext(), "+",
RubyBignum.newBignum(runtime, encode.get(pos) & 0x7f));
if(v instanceof RubyFixnum) {
big = RubyBignum.newBignum(runtime, RubyNumeric.fix2long(v));
} else if (v instanceof RubyBignum) {
big = (RubyBignum)v;
}
if((encode.get(pos++) & 0x80) == 0) {
IRubyObject value = RubyBignum.bignorm(runtime, big.getValue());
if (mode == UNPACK_1) {
return value;
}
appendOrYield(context, block, result, value, mode);
occurrences--;
ul = 0;
break;
}
}
}
}
try {
positionBuffer(encode, pos);
} catch (IllegalArgumentException e) {
throw runtime.newArgumentError("in `unpack': poorly encoded input");
}
return context.nil;
}
private static void unpack_x(Ruby runtime, ByteBuffer encode, int occurrences) {
if (occurrences == IS_STAR) {
occurrences = encode.remaining();
}
try {
positionBuffer(encode, encode.position() + occurrences);
} catch (IllegalArgumentException e) {
throw runtime.newArgumentError("in `unpack': x outside of string");
}
}
private static void unpack_X(Ruby runtime, ByteBuffer encode, int occurrences) {
if (occurrences == IS_STAR) {
occurrences = encode.remaining();
}
try {
positionBuffer(encode, encode.position() - occurrences);
} catch (IllegalArgumentException e) {
throw runtime.newArgumentError("in `unpack': X outside of string");
}
}
private static IRubyObject unpack_U(ThreadContext context, Block block, Ruby runtime, RubyArray result, ByteBuffer encode, int occurrences, int mode) {
if (occurrences == IS_STAR || occurrences > encode.remaining()) {
occurrences = encode.remaining();
}
while (occurrences-- > 0 && encode.remaining() > 0) {
try {
RubyFixnum item = runtime.newFixnum(utf8Decode(encode));
if (mode == UNPACK_1) {
return item;
}
appendOrYield(context, block, result, item, mode);
} catch (IllegalArgumentException e) {
throw runtime.newArgumentError(e.getMessage());
}
}
return context.nil;
}
private static IRubyObject unpack_M(ThreadContext context, Block block, RubyArray result, boolean tainted, ByteBuffer encode, int mode) {
byte[] lElem = new byte[Math.max(encode.remaining(),0)];
int index = 0;
for(;;) {
if (!encode.hasRemaining()) break;
int c = safeGet(encode);
if (c != '=') {
lElem[index++] = (byte)c;
} else {
if (!encode.hasRemaining()) break;
markBuffer(encode);
int c1 = safeGet(encode);
if (c1 == '\n' || (c1 == '\r' && (c1 = safeGet(encode)) == '\n')) continue;
int d1 = Character.digit(c1, 16);
if (d1 == -1) {
encode.reset();
break;
}
markBuffer(encode);
if (!encode.hasRemaining()) break;
int c2 = safeGet(encode);
int d2 = Character.digit(c2, 16);
if (d2 == -1) {
encode.reset();
break;
}
byte value = (byte)(d1 << 4 | d2);
lElem[index++] = value;
}
}
return appendOrYield(context, block, result, new ByteList(lElem, 0, index, ASCII, false), mode, tainted);
}
private static IRubyObject unpack_m(ThreadContext context, Block block, Ruby runtime, RubyArray result, boolean tainted, ByteBuffer encode, int occurrences, int mode) {
int length = encode.remaining()*3/4;
byte[] lElem = new byte[length];
int a = -1, b = -1, c = 0, d;
int index = 0;
int s = -1;
if (occurrences == 0){
index = unpack_m_zeroOccurrences(runtime, encode, lElem, a, b, c, index, s);
} else {
index = unpack_m_nonzeroOccurrences(encode, lElem, a, b, c, index);
}
return appendOrYield(context, block, result, new ByteList(lElem, 0, index, ASCII, false), mode, tainted);
}
private static int unpack_m_nonzeroOccurrences(ByteBuffer encode, byte[] lElem, int a, int b, int c, int index) {
int d;
int s;
while (encode.hasRemaining()) {
a = b = c = d = -1;
s = safeGet(encode);
while (((a = b64_xtable[s]) == -1) && encode.hasRemaining()) {
s = safeGet(encode);
}
if (a == -1) break;
s = safeGet(encode);
while (((b = b64_xtable[s]) == -1) && encode.hasRemaining()) {
s = safeGet(encode);
}
if (b == -1) break;
s = safeGet(encode);
while (((c = b64_xtable[s]) == -1) && encode.hasRemaining()) {
if (s == '=') break;
s = safeGet(encode);
}
if ((s == '=') || c == -1) {
if (s == '=') {
positionBuffer(encode, encode.position() - 1);
}
break;
}
s = safeGet(encode);
while (((d = b64_xtable[s]) == -1) && encode.hasRemaining()) {
if (s == '=') break;
s = safeGet(encode);
}
if ((s == '=') || d == -1) {
if (s == '=') {
positionBuffer(encode, encode.position() - 1);
}
break;
}
lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
lElem[index++] = (byte)((b << 4 | c >> 2) & 255);
lElem[index++] = (byte)((c << 6 | d) & 255);
a = -1;
}
if (a != -1 && b != -1) {
if (c == -1) {
lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
} else {
lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
lElem[index++] = (byte)((b << 4 | c >> 2) & 255);
}
}
return index;
}
private static int unpack_m_zeroOccurrences(Ruby runtime, ByteBuffer encode, byte[] lElem, int a, int b, int c, int index, int s) {
int d;
if (encode.remaining()%4 != 0) {
throw runtime.newArgumentError("invalid base64");
}
while (encode.hasRemaining() && s != '=') {
a = b = c = -1;
d = -2;
s = safeGet(encode);
a = b64_xtable[s];
if (a == -1) throw runtime.newArgumentError("invalid base64");
s = safeGet(encode);
b = b64_xtable[s];
if (b == -1) throw runtime.newArgumentError("invalid base64");
s = safeGet(encode);
c = b64_xtable[s];
if (s == '=') {
if (safeGet(encode) != '=') throw runtime.newArgumentError("invalid base64");
break;
}
if (c == -1) throw runtime.newArgumentError("invalid base64");
s = safeGet(encode);
d = b64_xtable[s];
if (s == '=') break;
if (d == -1) throw runtime.newArgumentError("invalid base64");
lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
lElem[index++] = (byte)((b << 4 | c >> 2) & 255);
lElem[index++] = (byte)((c << 6 | d) & 255);
}
if (encode.hasRemaining()) throw runtime.newArgumentError("invalid base64");
if (a != -1 && b != -1) {
if (c == -1 && s == '=') {
if ((b & 15) > 0) throw runtime.newArgumentError("invalid base64");
lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
} else if(c != -1 && s == '=') {
if ((c & 3) > 0) throw runtime.newArgumentError("invalid base64");
lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
lElem[index++] = (byte)((b << 4 | c >> 2) & 255);
}
}
return index;
}
private static IRubyObject unpack_u(ThreadContext context, Block block, RubyArray result, boolean tainted, ByteBuffer encode, int mode) {
int length = encode.remaining() * 3 / 4;
byte[] lElem = new byte[length];
int index = 0;
int s = 0;
int total = 0;
if (length > 0) s = encode.get();
while (encode.hasRemaining() && s > ' ' && s < 'a') {
int a, b, c, d;
byte[] hunk = new byte[3];
int len = (s - ' ') & 077;
s = safeGet(encode);
total += len;
if (total > length) {
len -= total - length;
total = length;
}
while (len > 0) {
int mlen = len > 3 ? 3 : len;
if (encode.hasRemaining() && s >= ' ') {
a = (s - ' ') & 077;
s = safeGet(encode);
} else
a = 0;
if (encode.hasRemaining() && s >= ' ') {
b = (s - ' ') & 077;
s = safeGet(encode);
} else
b = 0;
if (encode.hasRemaining() && s >= ' ') {
c = (s - ' ') & 077;
s = safeGet(encode);
} else
c = 0;
if (encode.hasRemaining() && s >= ' ') {
d = (s - ' ') & 077;
s = safeGet(encode);
} else
d = 0;
hunk[0] = (byte)((a << 2 | b >> 4) & 255);
hunk[1] = (byte)((b << 4 | c >> 2) & 255);
hunk[2] = (byte)((c << 6 | d) & 255);
for (int i = 0; i < mlen; i++) lElem[index++] = hunk[i];
len -= mlen;
}
if (s == '\r') {
s = safeGet(encode);
}
if (s == '\n') {
s = safeGet(encode);
}
else if (encode.hasRemaining()) {
if (safeGet(encode) == '\n') {
safeGet(encode);
} else if (encode.hasRemaining()) {
positionBuffer(encode, encode.position() - 1);
}
}
}
return appendOrYield(context, block, result, new ByteList(lElem, 0, index, ASCII, false), mode, tainted);
}
private static IRubyObject unpack_H(ThreadContext context, Block block, RubyArray result, boolean tainted, ByteBuffer encode, int occurrences, int mode) {
if (occurrences == IS_STAR || occurrences > encode.remaining() * 2) {
occurrences = encode.remaining() * 2;
}
int bits = 0;
byte[] lElem = new byte[occurrences];
for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) {
if ((lCurByte & 1) != 0) {
bits <<= 4;
} else {
bits = encode.get();
}
lElem[lCurByte] = sHexDigits[(bits >>> 4) & 15];
}
return appendOrYield(context, block, result, new ByteList(lElem, USASCII, false), mode, tainted);
}
private static IRubyObject unpack_h(ThreadContext context, Block block, RubyArray result, boolean tainted, ByteBuffer encode, int occurrences, int mode) {
if (occurrences == IS_STAR || occurrences > encode.remaining() * 2) {
occurrences = encode.remaining() * 2;
}
int bits = 0;
byte[] lElem = new byte[occurrences];
for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) {
if ((lCurByte & 1) != 0) {
bits >>>= 4;
} else {
bits = encode.get();
}
lElem[lCurByte] = sHexDigits[bits & 15];
}
return appendOrYield(context, block, result, new ByteList(lElem, USASCII, false), mode, tainted);
}
private static IRubyObject unpack_B(ThreadContext context, Block block, RubyArray result, boolean tainted, ByteBuffer encode, int occurrences, int mode) {
if (occurrences == IS_STAR || occurrences > encode.remaining() * 8) {
occurrences = encode.remaining() * 8;
}
int bits = 0;
byte[] lElem = new byte[occurrences];
for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) {
if ((lCurByte & 7) != 0) {
bits <<= 1;
} else {
bits = encode.get();
}
lElem[lCurByte] = (bits & 128) != 0 ? (byte)'1' : (byte)'0';
}
return appendOrYield(context, block, result, new ByteList(lElem, ASCII, false), mode, tainted);
}
private static IRubyObject unpack_b(ThreadContext context, Block block, RubyArray result, boolean tainted, ByteBuffer encode, int occurrences, int mode) {
if (occurrences == IS_STAR || occurrences > encode.remaining() * 8) {
occurrences = encode.remaining() * 8;
}
int bits = 0;
byte[] lElem = new byte[occurrences];
for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) {
if ((lCurByte & 7) != 0) {
bits >>>= 1;
} else {
bits = encode.get();
}
lElem[lCurByte] = (bits & 1) != 0 ? (byte)'1' : (byte)'0';
}
return appendOrYield(context, block, result, new ByteList(lElem, USASCII, false), mode, tainted);
}
private static IRubyObject unpack_a(ThreadContext context, Block block, RubyArray result, boolean tainted, ByteBuffer encode, int occurrences, int mode) {
if (occurrences == IS_STAR || occurrences > encode.remaining()) {
occurrences = encode.remaining();
}
byte[] potential = new byte[occurrences];
encode.get(potential);
return appendOrYield(context, block, result, new ByteList(potential, ASCII, false), mode,tainted);
}
private static IRubyObject unpack_Z(ThreadContext context, Block block, RubyArray result, boolean tainted, ByteBuffer encode, int occurrences, int mode) {
boolean isStar = (occurrences == IS_STAR);
if (occurrences == IS_STAR || occurrences > encode.remaining()) {
occurrences = encode.remaining();
}
byte[] potential = new byte[occurrences];
int t = 0;
while (t < occurrences) {
byte b = encode.get();
if (b == 0) {
break;
}
potential[t] = b;
t++;
}
IRubyObject value = appendOrYield(context, block, result, new ByteList(potential, 0, t, ASCII, false), mode, tainted);
if (mode == UNPACK_1) {
return value;
}
if (!isStar) {
if (t < occurrences) {
t++;
}
while (t < occurrences) {
encode.get();
t++;
}
}
return context.nil;
}
private static IRubyObject unpack_A(ThreadContext context, Block block, RubyArray result, boolean tainted, ByteBuffer encode, int occurrences, int mode) {
if (occurrences == IS_STAR || occurrences > encode.remaining()) {
occurrences = encode.remaining();
}
byte[] potential = new byte[occurrences];
encode.get(potential);
for (int t = occurrences - 1; occurrences > 0; occurrences--, t--) {
byte c = potential[t];
if (c != '\0' && c != ' ') {
break;
}
}
return appendOrYield(context, block, result, new ByteList(potential, 0, occurrences, ASCII, false), mode, tainted);
}
private static void unpack_at(Ruby runtime, ByteList encodedString, ByteBuffer encode, int occurrences) {
try {
if (occurrences == IS_STAR) {
positionBuffer(encode, encodedString.begin() + encode.remaining());
} else {
positionBuffer(encode, encodedString.begin() + occurrences);
}
} catch (IllegalArgumentException iae) {
throw runtime.newArgumentError("@ outside of string");
}
}
@Deprecated
public static RubyArray unpackWithBlock(ThreadContext context, Ruby runtime, ByteList encodedString, ByteList formatString, Block block) {
return unpackWithBlock(context, RubyString.newStringLight(runtime, encodedString), formatString, block);
}
private static void appendOrYield(ThreadContext context, Block block, RubyArray result, IRubyObject item, int mode) {
if (mode == UNPACK_BLOCK) {
block.yield(context, item);
} else if (mode == UNPACK_ARRAY) {
result.append(item);
}
}
private static IRubyObject appendOrYield(ThreadContext context, Block block, RubyArray result, ByteList item, int mode, boolean taint) {
RubyString itemStr = RubyString.newString(context.runtime, item);
if (taint) itemStr.setTaint(true);
if (mode == UNPACK_1) {
return itemStr;
} else {
appendOrYield(context, block, result, itemStr, mode);
return context.nil;
}
}
public static int utf8Decode(Ruby runtime, byte[]to, int p, int code) {
if (code <= 0x7f) {
to[p] = (byte)code;
return 1;
}
if (code <= 0x7ff) {
to[p + 0] = (byte)(((code >>> 6) & 0xff) | 0xc0);
to[p + 1] = (byte)((code & 0x3f) | 0x80);
return 2;
}
if (code <= 0xffff) {
to[p + 0] = (byte)(((code >>> 12) & 0xff) | 0xe0);
to[p + 1] = (byte)(((code >>> 6) & 0x3f) | 0x80);
to[p + 2] = (byte)((code & 0x3f) | 0x80);
return 3;
}
if (code <= 0x1fffff) {
to[p + 0] = (byte)(((code >>> 18) & 0xff) | 0xf0);
to[p + 1] = (byte)(((code >>> 12) & 0x3f) | 0x80);
to[p + 2] = (byte)(((code >>> 6) & 0x3f) | 0x80);
to[p + 3] = (byte)((code & 0x3f) | 0x80);
return 4;
}
if (code <= 0x3ffffff) {
to[p + 0] = (byte)(((code >>> 24) & 0xff) | 0xf8);
to[p + 1] = (byte)(((code >>> 18) & 0x3f) | 0x80);
to[p + 2] = (byte)(((code >>> 12) & 0x3f) | 0x80);
to[p + 3] = (byte)(((code >>> 6) & 0x3f) | 0x80);
to[p + 4] = (byte)((code & 0x3f) | 0x80);
return 5;
}
if (code <= 0x7fffffff) {
to[p + 0] = (byte)(((code >>> 30) & 0xff) | 0xfc);
to[p + 1] = (byte)(((code >>> 24) & 0x3f) | 0x80);
to[p + 2] = (byte)(((code >>> 18) & 0x3f) | 0x80);
to[p + 3] = (byte)(((code >>> 12) & 0x3f) | 0x80);
to[p + 4] = (byte)(((code >>> 6) & 0x3f) | 0x80);
to[p + 5] = (byte)((code & 0x3f) | 0x80);
return 6;
}
throw runtime.newRangeError("pack(U): value out of range");
}
private static int utf8Decode(ByteBuffer buffer) {
int c = buffer.get() & 0xFF;
int uv = c;
int n;
if ((c & 0x80) == 0) {
return c;
}
if ((c & 0x40) == 0) {
throw new IllegalArgumentException("malformed UTF-8 character");
}
if ((uv & 0x20) == 0) { n = 2; uv &= 0x1f; }
else if ((uv & 0x10) == 0) { n = 3; uv &= 0x0f; }
else if ((uv & 0x08) == 0) { n = 4; uv &= 0x07; }
else if ((uv & 0x04) == 0) { n = 5; uv &= 0x03; }
else if ((uv & 0x02) == 0) { n = 6; uv &= 0x01; }
else {
throw new IllegalArgumentException("malformed UTF-8 character");
}
if (n > buffer.remaining() + 1) {
throw new IllegalArgumentException(
"malformed UTF-8 character (expected " + n + " bytes, "
+ "given " + (buffer.remaining() + 1) + " bytes)");
}
int limit = n - 1;
n--;
if (n != 0) {
while (n-- != 0) {
c = buffer.get() & 0xff;
if ((c & 0xc0) != 0x80) {
throw new IllegalArgumentException("malformed UTF-8 character");
}
else {
c &= 0x3f;
uv = uv << 6 | c;
}
}
}
if (uv < utf8_limits[limit]) {
throw new IllegalArgumentException("redundant UTF-8 sequence");
}
return uv;
}
private static final long utf8_limits[] = {
0x0,
0x80,
0x800,
0x10000,
0x200000,
0x4000000,
0x80000000,
};
public static int safeGet(ByteBuffer encode) {
while (encode.hasRemaining()) {
int got = encode.get() & 0xff;
if (got != 0) return got;
}
return 0;
}
private static int safeGetIgnoreNull(ByteBuffer encode) {
int next = 0;
while (encode.hasRemaining() && next == 0) {
next = safeGet(encode);
}
return next;
}
public static IRubyObject decode(ThreadContext context, Ruby runtime, ByteBuffer encode, int occurrences,
RubyArray result, Block block, Converter converter, int mode) {
int lPadLength = 0;
if (occurrences == IS_STAR) {
occurrences = encode.remaining() / converter.size;
} else if (occurrences > encode.remaining() / converter.size) {
lPadLength = occurrences - encode.remaining() / converter.size;
occurrences = encode.remaining() / converter.size;
}
for (; occurrences-- > 0;) {
IRubyObject value = converter.decode(runtime, encode);
if (mode == UNPACK_1) {
return value;
}
appendOrYield(context, block, result, value, mode);
}
for (; lPadLength-- > 0;) {
if (mode == UNPACK_1) {
return context.nil;
}
appendOrYield(context, block, result, context.nil, mode);
}
return context.nil;
}
public static int encode(Ruby runtime, int occurrences, ByteList result,
RubyArray list, int index, ConverterExecutor converter) {
int listSize = list.size();
while (occurrences-- > 0) {
if (listSize-- <= 0 || index >= list.size()) {
throw runtime.newArgumentError(sTooFew);
}
IRubyObject from = list.eltInternal(index++);
converter.encode(runtime, from, result);
}
return index;
}
private abstract static class ConverterExecutor {
protected Converter converter;
public void setConverter(Converter converter) {
this.converter = converter;
}
public abstract IRubyObject decode(Ruby runtime, ByteBuffer format);
public abstract void encode(Ruby runtime, IRubyObject from, ByteList result);
}
private static ConverterExecutor executor() {
return new ConverterExecutor() {
@Override
public IRubyObject decode(Ruby runtime, ByteBuffer format) {
return converter.decode(runtime, format);
}
@Override
public void encode(Ruby runtime, IRubyObject from, ByteList result) {
if (from == runtime.getNil() && converter.getType() != null) throw runtime.newTypeError(from, converter.getType());
converter.encode(runtime, from, result);
}
};
}
public abstract static class Converter {
public int size;
public String type;
public Converter(int size) {
this(size, null);
}
public Converter(int size, String type) {
this.size = size;
this.type = type;
}
public String getType() {
return type;
}
public abstract IRubyObject decode(Ruby runtime, ByteBuffer format);
public abstract void encode(Ruby runtime, IRubyObject from, ByteList result);
}
private abstract static class QuadConverter extends Converter{
public QuadConverter(int size, String type) {
super(size, type);
}
public QuadConverter(int size) {
super(size);
}
protected int overflowQuad(long quad) {
return (int) (quad & 0xffff);
}
protected void encodeShortByByteOrder(ByteList result, int s) {
if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
encodeShortBigEndian(result, s);
} else {
encodeShortLittleEndian(result, s);
}
}
protected void encodeLongByByteOrder(ByteList result, long l) {
if (Platform.BYTE_ORDER == Platform.BIG_ENDIAN) {
encodeLongBigEndian(result, l);
} else {
encodeLongLittleEndian(result, l);
}
}
}
private static final ByteList shrink(ByteList i2Shrink, int iLength) {
iLength = i2Shrink.length() - iLength;
if (iLength < 0) {
throw new IllegalArgumentException();
}
i2Shrink.length(iLength);
return i2Shrink;
}
private static final ByteList grow(ByteList i2Grow, byte[]iPads, int iLength) {
int lPadLength = iPads.length;
while (iLength >= lPadLength) {
i2Grow.append(iPads);
iLength -= lPadLength;
}
i2Grow.append(iPads, 0, iLength);
return i2Grow;
}
public static RubyString pack(Ruby runtime, RubyArray list, ByteList formatString) {
return packCommon(runtime.getCurrentContext(), list, formatString, false, executor());
}
@Deprecated
public static RubyString pack(ThreadContext context, Ruby runtime, RubyArray list, RubyString formatString) {
return pack(context, list, formatString);
}
@Deprecated
public static void decode(ThreadContext context, Ruby runtime, ByteBuffer encode, int occurrences,
RubyArray result, Block block, Converter converter) {
decode(context, runtime, encode, occurrences,
result, block, converter, block.isGiven() ? UNPACK_BLOCK : UNPACK_ARRAY);
}
public static RubyString pack(ThreadContext context, RubyArray list, RubyString formatString) {
RubyString pack = packCommon(context, list, formatString.getByteList(), formatString.isTaint(), executor());
return (RubyString) pack.infectBy(formatString);
}
private static class PackInts {
PackInts(int listSize, int idx) {
this.listSize = listSize;
this.idx = idx;
}
int listSize;
int idx;
}
private static RubyString packCommon(ThreadContext context, RubyArray list, ByteList formatString, boolean tainted, ConverterExecutor executor) {
ByteBuffer format = ByteBuffer.wrap(formatString.getUnsafeBytes(), formatString.begin(), formatString.length());
ByteList result = new ByteList();
boolean taintOutput = tainted;
PackInts packInts = new PackInts(list.size(), 0);
int type;
int next = safeGet(format);
int enc_info = 1;
mainLoop: while (next != 0) {
type = next;
next = safeGet(format);
if (PACK_IGNORE_NULL_CODES.indexOf(type) != -1 && next == 0) {
next = safeGetIgnoreNull(format);
}
while (ASCII.isSpace(type)) {
if (next == 0) break mainLoop;
type = next;
next = safeGet(format);
}
if (type == '#') {
while (type != '\n') {
if (next == 0) break mainLoop;
type = next;
next = safeGet(format);
}
}
if (next == '!' || next == '_') {
int index = NATIVE_CODES.indexOf(type);
if (index == -1) {
throw context.runtime.newArgumentError("'" + next + "' allowed only after types " + NATIVE_CODES);
}
int typeBeforeMap = type;
type = MAPPED_CODES.charAt(index);
next = safeGet(format);
if (PACK_IGNORE_NULL_CODES_WITH_MODIFIERS.indexOf(typeBeforeMap) != -1 && next == 0) {
next = safeGetIgnoreNull(format);
}
}
if (next == '>' || next == '<') {
next = next == '>' ? BE : LE;
int index = ENDIANESS_CODES.indexOf(type + next);
if (index == -1) {
throw context.runtime.newArgumentError("'" + (char) next + "' allowed only after types sSiIlLqQ");
}
type = ENDIANESS_CODES.charAt(index);
next = safeGet(format);
}
int occurrences = 1;
boolean isStar = false;
boolean ignoreStar = false;
if (next != 0) {
if (next == '*') {
if ("@XxumM".indexOf(type) != -1) {
occurrences = 0;
ignoreStar = true;
} else {
occurrences = list.size() - packInts.idx;
isStar = true;
}
next = safeGet(format);
} else if (ASCII.isDigit(next)) {
occurrences = 0;
do {
occurrences = occurrences * 10 + Character.digit((char)(next & 0xFF), 10);
next = safeGet(format);
} while (next != 0 && ASCII.isDigit(next));
}
}
enc_info = adjustEncInfo(type, enc_info);
Converter converter = converters[type];
if (converter != null) {
executor.setConverter(converter);
packInts.idx = encode(context.runtime, occurrences, result, list, packInts.idx, executor);
continue;
}
switch (type) {
case '%':
throw context.runtime.newArgumentError("% is not supported");
case 'A':
case 'a':
case 'Z':
case 'B':
case 'b':
case 'H':
case 'h':
taintOutput = pack_h(context, list, result, taintOutput, packInts, type, occurrences, isStar);
break;
case 'x':
grow(result, sNil10, occurrences);
break;
case 'X':
pack_X(context, result, occurrences);
break;
case '@':
pack_at(result, occurrences);
break;
case 'u':
case 'm':
taintOutput = pack_m(context, list, result, taintOutput, packInts, (char) type, occurrences, ignoreStar);
break;
case 'M':
taintOutput = pack_M(context, list, result, taintOutput, packInts, occurrences);
break;
case 'U':
pack_U(context, list, result, packInts, occurrences);
break;
case 'w':
pack_w(context, list, result, packInts, occurrences);
break;
}
}
RubyString output = RubyString.newString(context.runtime, result);
if (taintOutput) output.setTaint(true);
switch (enc_info) {
case 1:
output.setEncodingAndCodeRange(USASCII, StringSupport.CR_7BIT);
break;
case 2:
output.associateEncoding(UTF8);
break;
default:
}
return output;
}
private static void pack_w(ThreadContext context, RubyArray list, ByteList result, PackInts packInts, int occurrences) {
while (occurrences-- > 0) {
if (packInts.listSize-- <= 0) throw context.runtime.newArgumentError(sTooFew);
IRubyObject from = list.eltInternal(packInts.idx++);
if (from == context.nil) throw context.runtime.newTypeError("pack('w') does not take nil");
final ByteList buf = new ByteList();
if (from instanceof RubyBignum) {
RubyBignum big128 = RubyBignum.newBignum(context.runtime, 128);
while (from instanceof RubyBignum) {
RubyArray ary = (RubyArray) ((RubyBignum) from).divmod(context, big128);
buf.append((byte) (RubyNumeric.fix2int(ary.eltInternal(1)) | 0x80) & 0xff);
from = ary.eltInternal(0);
}
}
long l = RubyNumeric.num2long(from);
if (l >= 0) {
while(l != 0) {
buf.append((byte)(((l & 0x7f) | 0x80) & 0xff));
l >>= 7;
}
int left = 0;
int right = buf.getRealSize() - 1;
if (right >= 0) {
buf.getUnsafeBytes()[0] &= 0x7F;
} else {
buf.append(0);
}
while (left < right) {
byte tmp = buf.getUnsafeBytes()[left];
buf.getUnsafeBytes()[left] = buf.getUnsafeBytes()[right];
buf.getUnsafeBytes()[right] = tmp;
left++;
right--;
}
result.append(buf);
} else {
throw context.runtime.newArgumentError("can't compress negative numbers");
}
}
}
private static void pack_U(ThreadContext context, RubyArray list, ByteList result, PackInts packInts, int occurrences) {
while (occurrences-- > 0) {
if (packInts.listSize-- <= 0) throw context.runtime.newArgumentError(sTooFew);
IRubyObject from = list.eltInternal(packInts.idx++);
int code = from == context.nil ? 0 : RubyNumeric.num2int(from);
if (code < 0) throw context.runtime.newRangeError("pack(U): value out of range");
int len = result.getRealSize();
result.ensure(len + 6);
result.setRealSize(len + utf8Decode(context.runtime, result.getUnsafeBytes(), result.getBegin() + len, code));
}
}
private static boolean pack_M(ThreadContext context, RubyArray list, ByteList result, boolean taintOutput, PackInts packInts, int occurrences) {
ByteList lCurElemString;
if (packInts.listSize-- <= 0) throw context.runtime.newArgumentError(sTooFew);
IRubyObject from = list.eltInternal(packInts.idx++);
if (from.isTaint()) taintOutput = true;
lCurElemString = from == context.nil ? ByteList.EMPTY_BYTELIST : from.asString().getByteList();
if (occurrences <= 1) {
occurrences = 72;
}
PackUtils.qpencode(result, lCurElemString, occurrences);
return taintOutput;
}
private static boolean pack_h(ThreadContext context, RubyArray list, ByteList result, boolean taintOutput, PackInts packInts, int type, int occurrences, boolean isStar) {
ByteList lCurElemString;
if (packInts.listSize-- <= 0) {
throw context.runtime.newArgumentError(sTooFew);
}
IRubyObject from = list.eltInternal(packInts.idx++);
lCurElemString = from == context.nil ? ByteList.EMPTY_BYTELIST : from.convertToString().getByteList();
if (from.isTaint()) taintOutput = true;
if (isStar) {
occurrences = lCurElemString.length();
if (type == 'Z') occurrences++;
}
pack_h_inner(result, type, lCurElemString, occurrences);
return taintOutput;
}
private static boolean pack_m(ThreadContext context, RubyArray list, ByteList result, boolean taintOutput, PackInts packInts, char type, int occurrences, boolean ignoreStar) {
ByteList lCurElemString;
if (packInts.listSize-- <= 0) throw context.runtime.newArgumentError(sTooFew);
IRubyObject from = list.eltInternal(packInts.idx++);
if (from == context.nil) throw context.runtime.newTypeError(from, "Integer");
lCurElemString = from.convertToString().getByteList();
if (from.isTaint()) taintOutput = true;
encodeUM(context.runtime, lCurElemString, occurrences, ignoreStar, type, result);
return taintOutput;
}
private static void pack_at(ByteList result, int occurrences) {
occurrences -= result.length();
if (occurrences > 0) {
grow(result, sNil10, occurrences);
}
occurrences = -occurrences;
if (occurrences > 0) {
shrink(result, occurrences);
}
}
private static void pack_X(ThreadContext context, ByteList result, int occurrences) {
try {
shrink(result, occurrences);
} catch (IllegalArgumentException e) {
throw context.runtime.newArgumentError("in `pack': X outside of string");
}
}
private static void pack_h_inner(ByteList result, int type, ByteList lCurElemString, int occurrences) {
switch (type) {
case 'a' :
case 'A' :
case 'Z' :
pack_h_aAZ(result, type, lCurElemString, occurrences);
break;
case 'b' :
pack_h_b(result, lCurElemString, occurrences);
break;
case 'B' :
pack_h_B(result, lCurElemString, occurrences);
break;
case 'h' :
pack_h_h(result, lCurElemString, occurrences);
break;
case 'H' :
pack_h_H(result, lCurElemString, occurrences);
break;
}
}
private static void pack_h_H(ByteList result, ByteList lCurElemString, int occurrences) {
int currentByte = 0;
int padLength = 0;
if (occurrences > lCurElemString.length()) {
padLength = occurrences - lCurElemString.length() + 1;
occurrences = lCurElemString.length();
}
for (int i = 0; i < occurrences;) {
byte currentChar = (byte)lCurElemString.charAt(i++);
if (Character.isJavaIdentifierStart(currentChar)) {
currentByte |= ((currentChar & 15) + 9) & 15;
} else {
currentByte |= currentChar & 15;
}
if ((i & 1) != 0) {
currentByte <<= 4;
} else {
result.append((byte) (currentByte & 0xff));
currentByte = 0;
}
}
if ((occurrences & 1) != 0) {
result.append((byte) (currentByte & 0xff));
if (padLength > 0) padLength--;
}
result.length(result.length() + padLength / 2);
}
private static void pack_h_h(ByteList result, ByteList lCurElemString, int occurrences) {
int currentByte = 0;
int padLength = 0;
if (occurrences > lCurElemString.length()) {
padLength = occurrences - lCurElemString.length() + 1;
occurrences = lCurElemString.length();
}
for (int i = 0; i < occurrences;) {
byte currentChar = (byte)lCurElemString.charAt(i++);
if (Character.isJavaIdentifierStart(currentChar)) {
currentByte |= (((currentChar & 15) + 9) & 15) << 4;
} else {
currentByte |= (currentChar & 15) << 4;
}
if ((i & 1) != 0) {
currentByte >>= 4;
} else {
result.append((byte) (currentByte & 0xff));
currentByte = 0;
}
}
if ((occurrences & 1) != 0) {
result.append((byte) (currentByte & 0xff));
if (padLength > 0) padLength--;
}
result.length(result.length() + padLength / 2);
}
private static void pack_h_B(ByteList result, ByteList lCurElemString, int occurrences) {
int currentByte = 0;
int padLength = 0;
if (occurrences > lCurElemString.length()) {
padLength = (occurrences - lCurElemString.length()) / 2 + (occurrences + lCurElemString.length()) % 2;
occurrences = lCurElemString.length();
}
for (int i = 0; i < occurrences;) {
currentByte |= lCurElemString.charAt(i++) & 1;
if ((i & 7) == 0) {
result.append((byte) (currentByte & 0xff));
currentByte = 0;
continue;
}
currentByte <<= 1;
}
if ((occurrences & 7) != 0) {
currentByte <<= 7 - (occurrences & 7);
result.append((byte) (currentByte & 0xff));
}
result.length(result.length() + padLength);
}
private static void pack_h_b(ByteList result, ByteList lCurElemString, int occurrences) {
int currentByte = 0;
int padLength = 0;
if (occurrences > lCurElemString.length()) {
padLength = (occurrences - lCurElemString.length()) / 2 + (occurrences + lCurElemString.length()) % 2;
occurrences = lCurElemString.length();
}
for (int i = 0; i < occurrences;) {
if ((lCurElemString.charAt(i++) & 1) != 0) {
currentByte |= 128;
}
if ((i & 7) == 0) {
result.append((byte) (currentByte & 0xff));
currentByte = 0;
continue;
}
currentByte >>= 1;
}
if ((occurrences & 7) != 0) {
currentByte >>= 7 - (occurrences & 7);
result.append((byte) (currentByte & 0xff));
}
result.length(result.length() + padLength);
}
private static void pack_h_aAZ(ByteList result, int type, ByteList lCurElemString, int occurrences) {
if (lCurElemString.length() >= occurrences) {
result.append(lCurElemString.getUnsafeBytes(), lCurElemString.getBegin(), occurrences);
} else {
result.append(lCurElemString);
occurrences -= lCurElemString.length();
switch (type) {
case 'a':
case 'Z':
grow(result, sNil10, occurrences);
break;
default:
grow(result, sSp10, occurrences);
break;
}
}
}
private static int adjustEncInfo(int type, int enc_info) {
switch (type) {
case 'U':
if (enc_info == 1) enc_info = 2;
break;
case 'm':
case 'M':
case 'u':
break;
default:
enc_info = 0;
break;
}
return enc_info;
}
private static int decodeIntLittleEndian(ByteBuffer encode) {
encode.order(ByteOrder.LITTLE_ENDIAN);
int value = encode.getInt();
encode.order(ByteOrder.BIG_ENDIAN);
return value;
}
private static int decodeIntBigEndian(ByteBuffer encode) {
return encode.getInt();
}
private static long decodeIntUnsignedBigEndian(ByteBuffer encode) {
return (long)encode.getInt() & 0xFFFFFFFFL;
}
private static long decodeIntUnsignedLittleEndian(ByteBuffer encode) {
encode.order(ByteOrder.LITTLE_ENDIAN);
long value = encode.getInt() & 0xFFFFFFFFL;
encode.order(ByteOrder.BIG_ENDIAN);
return value;
}
private static void encodeIntLittleEndian(ByteList result, int s) {
result.append((byte) (s & 0xff)).append((byte) ((s >> 8) & 0xff));
result.append((byte) ((s>>16) & 0xff)).append((byte) ((s >> 24) & 0xff));
}
private static void encodeIntBigEndian(ByteList result, int s) {
result.append((byte) ((s>>24) &0xff)).append((byte) ((s>>16) &0xff));
result.append((byte) ((s >> 8) & 0xff)).append((byte) (s & 0xff));
}
private static long decodeLongBigEndian(ByteBuffer encode) {
int c1 = decodeIntBigEndian(encode);
int c2 = decodeIntBigEndian(encode);
return ((long) c1 << 32) + (c2 & 0xffffffffL);
}
private static long decodeLongLittleEndian(ByteBuffer encode) {
int c1 = decodeIntLittleEndian(encode);
int c2 = decodeIntLittleEndian(encode);
return ((long) c2 << 32) + (c1 & 0xffffffffL);
}
private static void encodeLongLittleEndian(ByteList result, long l) {
encodeIntLittleEndian(result, (int) (l & 0xffffffff));
encodeIntLittleEndian(result, (int) (l >>> 32));
}
private static void encodeLongBigEndian(ByteList result, long l) {
encodeIntBigEndian(result, (int) (l >>> 32));
encodeIntBigEndian(result, (int) (l & 0xffffffff));
}
private static double decodeDoubleLittleEndian(ByteBuffer encode) {
return Double.longBitsToDouble(decodeLongLittleEndian(encode));
}
private static double decodeDoubleBigEndian(ByteBuffer encode) {
return Double.longBitsToDouble(decodeLongBigEndian(encode));
}
private static void encodeDoubleLittleEndian(ByteList result, double d) {
encodeLongLittleEndian(result, Double.doubleToRawLongBits(d));
}
private static void encodeDoubleBigEndian(ByteList result, double d) {
encodeLongBigEndian(result, Double.doubleToRawLongBits(d));
}
private static float decodeFloatBigEndian(ByteBuffer encode) {
return Float.intBitsToFloat(decodeIntBigEndian(encode));
}
private static float decodeFloatLittleEndian(ByteBuffer encode) {
return Float.intBitsToFloat(decodeIntLittleEndian(encode));
}
private static void encodeFloatLittleEndian(ByteList result, float f) {
encodeIntLittleEndian(result, Float.floatToRawIntBits(f));
}
private static void encodeFloatBigEndian(ByteList result, float f) {
encodeIntBigEndian(result, Float.floatToRawIntBits(f));
}
private static int decodeShortUnsignedLittleEndian(ByteBuffer encode) {
encode.order(ByteOrder.LITTLE_ENDIAN);
int value = encode.getShort() & 0xFFFF;
encode.order(ByteOrder.BIG_ENDIAN);
return value;
}
private static int decodeShortUnsignedBigEndian(ByteBuffer encode) {
int value = encode.getShort() & 0xFFFF;
return value;
}
private static int decodeShortLittleEndian(ByteBuffer encode) {
encode.order(ByteOrder.LITTLE_ENDIAN);
int value = encode.getShort();
encode.order(ByteOrder.BIG_ENDIAN);
return value;
}
private static short decodeShortBigEndian(ByteBuffer encode) {
return encode.getShort();
}
private static void encodeShortLittleEndian(ByteList result, int s) {
result.append((byte) (s & 0xff)).append((byte) ((s & 0xff00) >> 8));
}
private static void encodeShortBigEndian(ByteList result, int s) {
result.append((byte) ((s & 0xff00) >> 8)).append((byte) (s & 0xff));
}
}