Skip to content

Commit

Permalink
Showing 42 changed files with 2,901 additions and 115 deletions.
8 changes: 4 additions & 4 deletions core/src/main/java/org/jruby/util/Pack.java
Original file line number Diff line number Diff line change
@@ -78,8 +78,8 @@ public class Pack {
private static final String sTooFew = "too few arguments";
private static final byte[] uu_table;
private static final byte[] b64_table;
private static final byte[] sHexDigits;
private static final int[] b64_xtable = new int[256];
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) {
@@ -1390,7 +1390,7 @@ public static int utf8Decode(Ruby runtime, byte[]to, int p, int code) {

/** utf8_to_uv
*/
private static int utf8Decode(ByteBuffer buffer) {
private static int utf8Decode(ByteBuffer buffer) {
int c = buffer.get() & 0xFF;
int uv = c;
int n;
@@ -1451,7 +1451,7 @@ private static int utf8Decode(ByteBuffer buffer) {
0x80000000, /* 7 */
};

private static int safeGet(ByteBuffer encode) {
public static int safeGet(ByteBuffer encode) {
while (encode.hasRemaining()) {
int got = encode.get() & 0xff;

1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/util/cli/Options.java
Original file line number Diff line number Diff line change
@@ -240,6 +240,7 @@ public class Options {
public static final Option<Integer> TRUFFLE_SYMBOL_TO_PROC_CACHE = integer(TRUFFLE, "truffle.symbol_to_proc.cache", TRUFFLE_DEFAULT_CACHE.load(), "Symbol#to_proc cache size");
public static final Option<Integer> TRUFFLE_ALLOCATE_CLASS_CACHE = integer(TRUFFLE, "truffle.allocate_class.cache", TRUFFLE_DEFAULT_CACHE.load(), "Allocation size class cache size");
public static final Option<Integer> TRUFFLE_PACK_CACHE = integer(TRUFFLE, "truffle.pack.cache", TRUFFLE_DEFAULT_CACHE.load(), "Array#pack cache size");
public static final Option<Integer> TRUFFLE_UNPACK_CACHE = integer(TRUFFLE, "truffle.unpack.cache", TRUFFLE_DEFAULT_CACHE.load(), "String#unpack cache size");
public static final Option<Integer> TRUFFLE_EVAL_CACHE = integer(TRUFFLE, "truffle.eval.cache", TRUFFLE_DEFAULT_CACHE.load(), "eval lookup cache size");
public static final Option<Integer> ENCODING_COMPATIBLE_QUERY_CACHE = integer(TRUFFLE, "truffle.encoding_compatible_query.cache", TRUFFLE_DEFAULT_CACHE.load(), "Encoding.compatible? cache size");

2 changes: 2 additions & 0 deletions spec/truffle/tags/core/string/unpack/m_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
fails:String#unpack with format 'M' calls #to_str to coerce the directives string
fails:String#unpack with format 'm' calls #to_str to coerce the directives string
fails:String#unpack with format 'M' ignores the count or '*' modifier and decodes the entire string
fails:String#unpack with format 'm' ignores the count or '*' modifier and decodes the entire string
2 changes: 2 additions & 0 deletions spec/truffle/tags/core/string/unpack/z_tags.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
fails:String#unpack with format 'Z' calls #to_str to coerce the directives string
fails:String#unpack with format 'Z' stops decoding at NULL bytes when passed the '*' modifier
fails:String#unpack with format 'Z' decodes the number of bytes specified by the count modifier and truncates the decoded string at the first NULL byte
115 changes: 60 additions & 55 deletions truffle/src/main/antlr4/org/jruby/truffle/format/parser/Pack.g4
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
@@ -11,64 +11,69 @@ grammar Pack;

sequence : directive* ;

directive : C count? # character
| (S NATIVE? LITTLE | 'v') count? # shortLittle
| (S NATIVE? BIG | 'n') count? # shortBig
| S NATIVE? count? # shortNative
| I (LITTLE NATIVE? | NATIVE? LITTLE) count? # intLittle
| I (BIG NATIVE? | NATIVE? BIG) count? # intBig
| I NATIVE? count? # intNative
| (L LITTLE | 'V') count? # longLittle
| (L BIG | 'N') count? # longBig
| L count? # longNative
| (Q | L NATIVE) LITTLE count? # quadLittle
| (Q | L NATIVE) BIG count? # quadBig
| (Q | L NATIVE) count? # quadNative
| 'U' count? # utf8Character
| 'w' count? # berInteger
| D count? # doubleNative
| F count? # floatNative
| 'E' count? # doubleLittle
| 'e' count? # floatLittle
| 'G' count? # doubleBig
| 'g' count? # floatBig
| 'A' count? # binaryStringSpacePadded
| 'a' count? # binaryStringNullPadded
| 'Z' count? # binaryStringNullStar
| 'B' count? # bitStringMSBFirst
| 'b' count? # bitStringMSBLast
| 'H' count? # hexStringHighFirst
| 'h' count? # hexStringLowFirst
| 'u' count? # uuString
| 'M' INT? # mimeString
| 'm' count? # base64String
| ('p' | 'P') # pointer
| '@' INT? # at
| 'X' count? # back
| 'x' count? # nullByte
| subSequence # subSequenceAlternate
| ('v' | 'n' | 'V' | 'N' | 'U' | 'w' | D |
F | 'E' | 'e' | 'g' | 'G' | 'A' | 'a' |
directive : 'c' count? # int8
| 'C' count? # uint8
| 's' nativeOptLittle count? # int16Little
| 's' nativeOptBig count? # int16Big
| 's' NATIVE? count? # int16Native
| ('S' nativeOptLittle | 'v') count? # uint16Little
| ('S' nativeOptBig | 'n') count? # uint16Big
| 'S' NATIVE? count? # uint16Native
| ('i' nativeOptLittle | 'l' LITTLE) count? # int32Little
| ('i' nativeOptBig | 'l' BIG) count? # int32Big
| ('i' NATIVE? | 'l') count? # int32Native
| (('I' nativeOptLittle | 'L' LITTLE) | 'V') count? # uint32Little
| (('I' nativeOptBig | 'L' BIG) | 'N') count? # uint32Big
| ('I' NATIVE? | 'L') count? # uint32Native
| ('q' nativeOptLittle | 'l' nativeLittle) count? # int64Little
| ('q' nativeOptBig | 'l' nativeBig) count? # int64Big
| ('q' NATIVE? | 'l' NATIVE) count? # int64Native
| ('Q' nativeOptLittle | 'L' nativeLittle) count? # uint64Little
| ('Q' nativeOptBig | 'L' nativeBig) count? # uint64Big
| ('Q' NATIVE? | 'L' NATIVE) count? # uint64Native
| 'U' count? # utf8Character
| 'w' count? # berInteger
| ('d' | 'D') count? # f64Native
| ('f' | 'F') count? # f32Native
| 'E' count? # f64Little
| 'e' count? # f32Little
| 'G' count? # f64Big
| 'g' count? # f32Big
| 'A' count? # binaryStringSpacePadded
| 'a' count? # binaryStringNullPadded
| 'Z' count? # binaryStringNullStar
| 'B' count? # bitStringMSBFirst
| 'b' count? # bitStringMSBLast
| 'H' count? # hexStringHighFirst
| 'h' count? # hexStringLowFirst
| 'u' count? # uuString
| 'M' INT? # mimeString
| 'm' count? # base64String
| ('p' | 'P') # pointer
| '@' count? # at
| 'X' count? # back
| 'x' count? # nullByte
| subSequence # subSequenceAlternate
| ('v' | 'n' | 'V' | 'N' | 'U' | 'w' | 'd' | 'D' |
'f' | 'F' | 'E' | 'e' | 'g' | 'G' | 'A' | 'a' |
'Z' | 'B' | 'b' | 'H' | 'h' | 'u' | 'M' |
'm' | 'p' | 'P' | 'X' | 'x') NATIVE #errorDisallowedNative ;
'm' | 'p' | 'P' | 'X' | 'x') NATIVE #errorDisallowedNative ;

subSequence : '(' directive+ ')' INT? ;
count : INT | '*' ;

count : INT | '*' ;
subSequence : '(' directive+ ')' INT? ;

C : [cC] ;
S : [sS] ;
I : [iI] ;
L : [lL] ;
Q : [qQ] ;
D : [dD] ;
F : [fF] ;
nativeOptLittle : NATIVE* LITTLE NATIVE* ;
nativeOptBig : NATIVE* BIG NATIVE* ;

LITTLE : '<' ;
BIG : '>' ;
NATIVE : [_!] ;
nativeLittle : NATIVE+ LITTLE NATIVE* | NATIVE* LITTLE NATIVE+ ;
nativeBig : NATIVE+ BIG NATIVE* | NATIVE* BIG NATIVE+ ;

INT : [0-9]+ ;
LITTLE : '<' ;
BIG : '>' ;
NATIVE : [!_] ;

WS : [ \t\n\u000b\f\r\u0000]+ -> skip ;
COMMENT : '#' .*? (('\r'? '\n') | EOF) -> skip ;
INT : [0-9]+ ;

WS : [ \t\n\u000b\f\r\u0000]+ -> skip ;
COMMENT : '#' .*? (('\r'? '\n') | EOF) -> skip ;
38 changes: 32 additions & 6 deletions truffle/src/main/java/org/jruby/truffle/format/nodes/PackNode.java
Original file line number Diff line number Diff line change
@@ -77,24 +77,50 @@ protected void setSourcePosition(VirtualFrame frame, int position) {
* element.
*/
protected int advanceSourcePosition(VirtualFrame frame) {
return advanceSourcePosition(frame, 1);
}

protected int advanceSourcePosition(VirtualFrame frame, int count) {
final int sourcePosition = getSourcePosition(frame);

if (sourcePosition == getSourceLength(frame)) {
if (sourcePosition + count > getSourceLength(frame)) {
CompilerDirectives.transferToInterpreter();
throw new TooFewArgumentsException();
}

setSourcePosition(frame, sourcePosition + 1);
setSourcePosition(frame, sourcePosition + count);

return sourcePosition;
}

protected int advanceSourcePositionNoThrow(VirtualFrame frame) {
return advanceSourcePositionNoThrow(frame, 1, false);
}

protected int advanceSourcePositionNoThrow(VirtualFrame frame, int count, boolean consumePartial) {
final int sourcePosition = getSourcePosition(frame);

final int sourceLength = getSourceLength(frame);

if (sourcePosition + count > sourceLength) {
if (consumePartial) {
setSourcePosition(frame, sourceLength);
}

return -1;
}

setSourcePosition(frame, sourcePosition + count);

return sourcePosition;
}

/**
* Get the output array we are writing to.
*/
protected byte[] getOutput(VirtualFrame frame) {
protected Object getOutput(VirtualFrame frame) {
try {
return (byte[]) frame.getObject(PackFrameDescriptor.OUTPUT_SLOT);
return frame.getObject(PackFrameDescriptor.OUTPUT_SLOT);
} catch (FrameSlotTypeException e) {
throw new IllegalStateException(e);
}
@@ -105,7 +131,7 @@ protected byte[] getOutput(VirtualFrame frame) {
* compiled code - having to change the output array to resize is is a
* deoptimizing action.
*/
protected void setOutput(VirtualFrame frame, byte[] output) {
protected void setOutput(VirtualFrame frame, Object output) {
CompilerAsserts.neverPartOfCompilation();
frame.setObject(PackFrameDescriptor.OUTPUT_SLOT, output);
}
@@ -181,7 +207,7 @@ protected void writeNullBytes(VirtualFrame frame, int length) {
}

private byte[] ensureCapacity(VirtualFrame frame, int length) {
byte[] output = getOutput(frame);
byte[] output = (byte[]) getOutput(frame);
final int outputPosition = getOutputPosition(frame);

if (outputPosition + length > output.length) {
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.FrameSlotTypeException;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.format.runtime.PackEncoding;
import org.jruby.truffle.format.runtime.PackFrameDescriptor;
import org.jruby.truffle.format.runtime.PackResult;
import org.jruby.truffle.runtime.RubyLanguage;
import org.jruby.truffle.runtime.array.ArrayUtils;

public class UnpackRootNode extends RootNode {

private final String description;
private final PackEncoding encoding;

@Child private PackNode child;

@CompilationFinal private int expectedLength = ArrayUtils.capacity(0, 0);

public UnpackRootNode(String description, PackEncoding encoding, PackNode child) {
super(RubyLanguage.class, SourceSection.createUnavailable("unpack", description), PackFrameDescriptor.FRAME_DESCRIPTOR);
this.description = description;
this.encoding = encoding;
this.child = child;
}

@Override
public Object execute(VirtualFrame frame) {
frame.setObject(PackFrameDescriptor.SOURCE_SLOT, frame.getArguments()[0]);
frame.setInt(PackFrameDescriptor.SOURCE_LENGTH_SLOT, (int) frame.getArguments()[1]);
frame.setInt(PackFrameDescriptor.SOURCE_POSITION_SLOT, 0);
frame.setObject(PackFrameDescriptor.OUTPUT_SLOT, new Object[expectedLength]);
frame.setInt(PackFrameDescriptor.OUTPUT_POSITION_SLOT, 0);
frame.setBoolean(PackFrameDescriptor.TAINT_SLOT, false);

child.execute(frame);

final int outputLength;

try {
outputLength = frame.getInt(PackFrameDescriptor.OUTPUT_POSITION_SLOT);
} catch (FrameSlotTypeException e) {
throw new IllegalStateException(e);
}

if (outputLength > expectedLength) {
CompilerDirectives.transferToInterpreterAndInvalidate();
expectedLength = ArrayUtils.capacity(expectedLength, outputLength);
}

final Object[] output;

try {
output = (Object[]) frame.getObject(PackFrameDescriptor.OUTPUT_SLOT);
} catch (FrameSlotTypeException e) {
throw new IllegalStateException(e);
}

final boolean taint;

try {
taint = frame.getBoolean(PackFrameDescriptor.TAINT_SLOT);
} catch (FrameSlotTypeException e) {
throw new IllegalStateException(e);
}

return new PackResult(output, outputLength, taint, encoding);
}

@Override
public boolean isCloningAllowed() {
return true;
}

@Override
public String toString() {
return description;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.control;

import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.exceptions.OutsideOfStringException;
import org.jruby.truffle.runtime.RubyContext;

public class AtUnpackNode extends PackNode {

private final int position;

public AtUnpackNode(RubyContext context, int position) {
super(context);
this.position = position;
}

@Override
public Object execute(VirtualFrame frame) {
if (position > getSourceLength(frame)) {
throw new OutsideOfStringException();
}

setSourcePosition(frame, position);

return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.control;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.exceptions.OutsideOfStringException;
import org.jruby.truffle.runtime.RubyContext;

public class BackUnpackNode extends PackNode {

private boolean star;

public BackUnpackNode(RubyContext context, boolean star) {
super(context);
this.star = star;
}

@Override
public Object execute(VirtualFrame frame) {
final int position = getSourcePosition(frame);

if (star) {
final int remaining = getSourceLength(frame) - position;

final int target = position - remaining;

if (target < 0) {
CompilerDirectives.transferToInterpreter();
throw new OutsideOfStringException();
}

setSourcePosition(frame, target);
} else {
if (position == 0) {
CompilerDirectives.transferToInterpreter();
throw new OutsideOfStringException();
}

setSourcePosition(frame, position - 1);
}


return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.control;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.exceptions.OutsideOfStringException;
import org.jruby.truffle.runtime.RubyContext;

public class ForwardUnpackNode extends PackNode {

private boolean toEnd;

public ForwardUnpackNode(RubyContext context, boolean toEnd) {
super(context);
this.toEnd = toEnd;
}

@Override
public Object execute(VirtualFrame frame) {
if (toEnd) {
setSourcePosition(frame, getSourceLength(frame));
} else {
final int position = getSourcePosition(frame);

if (position + 1 > getSourceLength(frame)) {
throw new OutsideOfStringException();
}

setSourcePosition(frame, getSourcePosition(frame) + 1);
}

return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.decode;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.runtime.RubyContext;

@NodeChildren({
@NodeChild(value = "value", type = PackNode.class),
})
public abstract class DecodeByteNode extends PackNode {

public boolean signed;

public DecodeByteNode(RubyContext context, boolean signed) {
super(context);
this.signed = signed;
}

@Specialization(guards = "isNil(nil)")
public DynamicObject decode(VirtualFrame frame, DynamicObject nil) {
return nil;
}

@Specialization
public int decode(VirtualFrame frame, byte value) {
if (signed) {
return value;
} else {
return value & 0xff;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.decode;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.runtime.RubyContext;

@NodeChildren({
@NodeChild(value = "value", type = PackNode.class),
})
public abstract class DecodeFloat32Node extends PackNode {

public DecodeFloat32Node(RubyContext context) {
super(context);
}

@Specialization
public MissingValue decode(VirtualFrame frame, MissingValue missingValue) {
return missingValue;
}

@Specialization(guards = "isNil(nil)")
public DynamicObject decode(VirtualFrame frame, DynamicObject nil) {
return nil;
}

@Specialization
public float decode(VirtualFrame frame, int value) {
return Float.intBitsToFloat(value);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.decode;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.runtime.RubyContext;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@NodeChildren({
@NodeChild(value = "value", type = PackNode.class),
})
public abstract class DecodeFloat64Node extends PackNode {

public DecodeFloat64Node(RubyContext context) {
super(context);
}

@Specialization
public MissingValue decode(VirtualFrame frame, MissingValue missingValue) {
return missingValue;
}

@Specialization(guards = "isNil(nil)")
public DynamicObject decode(VirtualFrame frame, DynamicObject nil) {
return nil;
}

@Specialization
public double decode(VirtualFrame frame, long value) {
return Double.longBitsToDouble(value);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.decode;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.runtime.RubyContext;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@NodeChildren({
@NodeChild(value = "bytes", type = PackNode.class),
})
public abstract class DecodeInteger16BigNode extends PackNode {

public DecodeInteger16BigNode(RubyContext context) {
super(context);
}

@Specialization
public MissingValue decode(VirtualFrame frame, MissingValue missingValue) {
return missingValue;
}

@Specialization(guards = "isNil(nil)")
public DynamicObject decode(VirtualFrame frame, DynamicObject nil) {
return nil;
}

@Specialization
public short decode(VirtualFrame frame, byte[] bytes) {
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.order(ByteOrder.BIG_ENDIAN);
return buffer.getShort();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.decode;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.runtime.RubyContext;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@NodeChildren({
@NodeChild(value = "bytes", type = PackNode.class),
})
public abstract class DecodeInteger16LittleNode extends PackNode {

public DecodeInteger16LittleNode(RubyContext context) {
super(context);
}

@Specialization
public MissingValue decode(VirtualFrame frame, MissingValue missingValue) {
return missingValue;
}

@Specialization(guards = "isNil(nil)")
public DynamicObject decode(VirtualFrame frame, DynamicObject nil) {
return nil;
}

@Specialization
public short decode(VirtualFrame frame, byte[] bytes) {
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.order(ByteOrder.LITTLE_ENDIAN);
return buffer.getShort();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.decode;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.runtime.RubyContext;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@NodeChildren({
@NodeChild(value = "bytes", type = PackNode.class),
})
public abstract class DecodeInteger32BigNode extends PackNode {

public DecodeInteger32BigNode(RubyContext context) {
super(context);
}

@Specialization
public MissingValue decode(VirtualFrame frame, MissingValue missingValue) {
return missingValue;
}

@Specialization(guards = "isNil(nil)")
public DynamicObject decode(VirtualFrame frame, DynamicObject nil) {
return nil;
}

@Specialization
public int decode(VirtualFrame frame, byte[] bytes) {
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.order(ByteOrder.BIG_ENDIAN);
return buffer.getInt();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.decode;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.runtime.RubyContext;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@NodeChildren({
@NodeChild(value = "bytes", type = PackNode.class),
})
public abstract class DecodeInteger32LittleNode extends PackNode {

public DecodeInteger32LittleNode(RubyContext context) {
super(context);
}

@Specialization
public MissingValue decode(VirtualFrame frame, MissingValue missingValue) {
return missingValue;
}

@Specialization(guards = "isNil(nil)")
public DynamicObject decode(VirtualFrame frame, DynamicObject nil) {
return nil;
}

@Specialization
public int decode(VirtualFrame frame, byte[] bytes) {
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.order(ByteOrder.LITTLE_ENDIAN);
return buffer.getInt();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.decode;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.runtime.RubyContext;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@NodeChildren({
@NodeChild(value = "bytes", type = PackNode.class),
})
public abstract class DecodeInteger64BigNode extends PackNode {

public DecodeInteger64BigNode(RubyContext context) {
super(context);
}

@Specialization
public MissingValue decode(VirtualFrame frame, MissingValue missingValue) {
return missingValue;
}

@Specialization(guards = "isNil(nil)")
public DynamicObject decode(VirtualFrame frame, DynamicObject nil) {
return nil;
}

@Specialization
public long decode(VirtualFrame frame, byte[] bytes) {
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.order(ByteOrder.BIG_ENDIAN);
return buffer.getLong();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.decode;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.runtime.RubyContext;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@NodeChildren({
@NodeChild(value = "bytes", type = PackNode.class),
})
public abstract class DecodeInteger64LittleNode extends PackNode {

public DecodeInteger64LittleNode(RubyContext context) {
super(context);
}

@Specialization
public MissingValue decode(VirtualFrame frame, MissingValue missingValue) {
return missingValue;
}

@Specialization(guards = "isNil(nil)")
public DynamicObject decode(VirtualFrame frame, DynamicObject nil) {
return nil;
}

@Specialization
public long decode(VirtualFrame frame, byte[] bytes) {
final ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.order(ByteOrder.LITTLE_ENDIAN);
return buffer.getLong();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.read;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jcodings.Encoding;
import org.jruby.RubyBignum;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.nodes.core.FixnumOrBignumNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;

import java.math.BigInteger;
import java.nio.ByteBuffer;

/**
* Read a string that contains UU-encoded data and write as actual binary
* data.
*/
@NodeChildren({
@NodeChild(value = "source", type = SourceNode.class),
})
public abstract class ReadBERNode extends PackNode {

@Child private FixnumOrBignumNode fixnumOrBignumNode;

private final int length;
private final boolean ignoreStar;

public ReadBERNode(RubyContext context, int length, boolean ignoreStar) {
super(context);
fixnumOrBignumNode = FixnumOrBignumNode.create(context, null);
this.length = length;
this.ignoreStar = ignoreStar;
}

@Specialization
protected Object encode(VirtualFrame frame, byte[] source) {
CompilerDirectives.transferToInterpreter();

// TODO CS 28-Dec-15 should write our own optimizable version of BER

/*
* Copied from JRuby's Pack class.
*
* **** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
* Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
* Copyright (C) 2003-2004 Thomas E Enebo <enebo@acm.org>
* Copyright (C) 2004 Charles O Nutter <headius@headius.com>
* Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
* Copyright (C) 2005 Derek Berner <derek.berner@state.nm.us>
* Copyright (C) 2006 Evan Buswell <ebuswell@gmail.com>
* Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
* Copyright (C) 2009 Joseph LaFata <joe@quibb.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/

final ByteBuffer encode = ByteBuffer.wrap(source, getSourcePosition(frame), getSourceLength(frame) - getSourcePosition(frame));

long ul = 0;
long ulmask = (0xfe << 56) & 0xffffffff;
BigInteger big128 = BigInteger.valueOf(128);
int pos = encode.position();

ul <<= 7;
ul |= encode.get(pos) & 0x7f;
if((encode.get(pos++) & 0x80) == 0) {
setSourcePosition(frame, getSourcePosition(frame) + pos);
return ul;
} else if((ul & ulmask) == 0) {
BigInteger big = BigInteger.valueOf(ul);
while(pos < encode.limit()) {
BigInteger mulResult = big.multiply(big128);
BigInteger v = mulResult.add(BigInteger.valueOf(encode.get(pos) & 0x7f));
big = v;
if((encode.get(pos++) & 0x80) == 0) {
setSourcePosition(frame, getSourcePosition(frame) + pos);
return fixnumOrBignumNode.fixnumOrBignum(big);
}
}
}

try {
encode.position(pos);
} catch (IllegalArgumentException e) {
//throw runtime.newArgumentError("in `unpack': poorly encoded input");
throw new UnsupportedOperationException();
}

throw new UnsupportedOperationException();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.read;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jcodings.Encoding;
import org.jcodings.specific.ASCIIEncoding;
import org.jruby.RubyString;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.format.runtime.exceptions.FormatException;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.Pack;
import org.jruby.util.StringSupport;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@NodeChildren({
@NodeChild(value = "value", type = SourceNode.class),
})
public abstract class ReadBase64StringNode extends PackNode {

private final int requestedLength;

public ReadBase64StringNode(RubyContext context, int requestedLength) {
super(context);
this.requestedLength = requestedLength;
}

@Specialization
public Object read(VirtualFrame frame, byte[] source) {
// Bit string logic copied from jruby.util.Pack - see copyright and authorship there

final ByteBuffer encode = ByteBuffer.wrap(source, getSourcePosition(frame), getSourceLength(frame) - getSourcePosition(frame));

int occurrences = requestedLength;

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){
if (encode.remaining()%4 != 0) {
throw new FormatException("invalid base64");
}
while (encode.hasRemaining() && s != '=') {
a = b = c = -1;
d = -2;

// obtain a
s = Pack.safeGet(encode);
a = Pack.b64_xtable[s];
if (a == -1) throw new FormatException("invalid base64");

// obtain b
s = Pack.safeGet(encode);
b = Pack.b64_xtable[s];
if (b == -1) throw new FormatException("invalid base64");

// obtain c
s = Pack.safeGet(encode);
c = Pack.b64_xtable[s];
if (s == '=') {
if (Pack.safeGet(encode) != '=') throw new FormatException("invalid base64");
break;
}
if (c == -1) throw new FormatException("invalid base64");

// obtain d
s = Pack.safeGet(encode);
d = Pack.b64_xtable[s];
if (s == '=') break;
if (d == -1) throw new FormatException("invalid base64");

// calculate based on a, b, c and d
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 new FormatException("invalid base64");

if (a != -1 && b != -1) {
if (c == -1 && s == '=') {
if ((b & 15) > 0) throw new FormatException("invalid base64");
lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
} else if(c != -1 && s == '=') {
if ((c & 3) > 0) throw new FormatException("invalid base64");
lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
lElem[index++] = (byte)((b << 4 | c >> 2) & 255);
}
}
}
else {

while (encode.hasRemaining()) {
a = b = c = d = -1;

// obtain a
s = Pack.safeGet(encode);
while (((a = Pack.b64_xtable[s]) == -1) && encode.hasRemaining()) {
s = Pack.safeGet(encode);
}
if (a == -1) break;

// obtain b
s = Pack.safeGet(encode);
while (((b = Pack.b64_xtable[s]) == -1) && encode.hasRemaining()) {
s = Pack.safeGet(encode);
}
if (b == -1) break;

// obtain c
s = Pack.safeGet(encode);
while (((c = Pack.b64_xtable[s]) == -1) && encode.hasRemaining()) {
if (s == '=') break;
s = Pack.safeGet(encode);
}
if ((s == '=') || c == -1) {
if (s == '=') {
encode.position(encode.position() - 1);
}
break;
}

// obtain d
s = Pack.safeGet(encode);
while (((d = Pack.b64_xtable[s]) == -1) && encode.hasRemaining()) {
if (s == '=') break;
s = Pack.safeGet(encode);
}
if ((s == '=') || d == -1) {
if (s == '=') {
encode.position(encode.position() - 1);
}
break;
}

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

if (a != -1 && b != -1) {
if (c == -1 && s == '=') {
lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
} else if(c != -1 && s == '=') {
lElem[index++] = (byte)((a << 2 | b >> 4) & 255);
lElem[index++] = (byte)((b << 4 | c >> 2) & 255);
}
}
}

final Encoding encoding = Encoding.load("ASCII");
final ByteList result = new ByteList(lElem, 0, index, encoding, false);
setSourcePosition(frame, encode.position());

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), result, StringSupport.CR_UNKNOWN, null);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.read;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;

import java.util.Arrays;

@NodeChildren({
@NodeChild(value = "source", type = SourceNode.class),
})
public abstract class ReadBinaryStringNode extends PackNode {

final boolean readToEnd;
final boolean readToNull;
final int count;
final boolean trimTrailingSpaces;
final boolean trimTrailingNulls;

public ReadBinaryStringNode(RubyContext context, boolean readToEnd, boolean readToNull, int count, boolean trimTrailingSpaces, boolean trimTrailingNulls) {
super(context);
this.readToEnd = readToEnd;
this.readToNull = readToNull;
this.count = count;
this.trimTrailingSpaces = trimTrailingSpaces;
this.trimTrailingNulls = trimTrailingNulls;
}

@Specialization(guards = "isNull(source)")
public void read(VirtualFrame frame, Object source) {
CompilerDirectives.transferToInterpreter();

// Advance will handle the error
advanceSourcePosition(frame, count);

throw new IllegalStateException();
}

@Specialization
public Object read(VirtualFrame frame, byte[] source) {
final int start = getSourcePosition(frame);

int length;
ByteList result;

if (readToEnd) {
length = 0;

while (start + length < getSourceLength(frame) && (!readToNull || (start + length < getSourceLength(frame) && source[start + length] != 0))) {
length++;
}
} else if (readToNull) {
length = 0;

while (start + length < getSourceLength(frame) && length < count && (!readToNull || (start + length < getSourceLength(frame) && source[start + length] != 0))) {
length++;
}
} else {
length = count;

if (start + length >= getSourceLength(frame)) {
length = getSourceLength(frame) - start;
}
}

int usedLength = length;

while (usedLength > 0 && ((trimTrailingSpaces && source[start + usedLength - 1] == ' ') || (trimTrailingNulls && source[start + usedLength - 1] == 0))) {
usedLength--;
}

result = new ByteList(source, start, usedLength, true);

setSourcePosition(frame, start + length);

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), result, StringSupport.CR_UNKNOWN, null);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.read;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jcodings.Encoding;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@NodeChildren({
@NodeChild(value = "value", type = SourceNode.class),
})
public abstract class ReadBitStringNode extends PackNode {

private final ByteOrder byteOrder;
private final boolean star;
private final int length;

public ReadBitStringNode(RubyContext context, ByteOrder byteOrder, boolean star, int length) {
super(context);
this.byteOrder = byteOrder;
this.star = star;
this.length = length;
}

@Specialization
public Object read(VirtualFrame frame, byte[] source) {
// Bit string logic copied from jruby.util.Pack - see copyright and authorship there

final ByteBuffer encode = ByteBuffer.wrap(source, getSourcePosition(frame), getSourceLength(frame) - getSourcePosition(frame));

int occurrences = length;
byte[] lElem;

if (byteOrder == ByteOrder.BIG_ENDIAN) {
if (star || occurrences > encode.remaining() * 8) {
occurrences = encode.remaining() * 8;
}
int bits = 0;
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';
}
} else {
if (star || occurrences > encode.remaining() * 8) {
occurrences = encode.remaining() * 8;
}
int bits = 0;
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';
}
}

final Encoding encoding = Encoding.load("ASCII");
final ByteList result = new ByteList(lElem, encoding, false);
setSourcePosition(frame, encode.position());

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), result, StringSupport.CR_UNKNOWN, null);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.read;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.runtime.RubyContext;

import java.util.Arrays;

@NodeChildren({
@NodeChild(value = "source", type = SourceNode.class),
})
public abstract class ReadByteNode extends PackNode {

public ReadByteNode(RubyContext context) {
super(context);
}

@Specialization(guards = "isNull(source)")
public void read(VirtualFrame frame, Object source) {
CompilerDirectives.transferToInterpreter();

// Advance will handle the error
advanceSourcePosition(frame, 1);

throw new IllegalStateException();
}

@Specialization
public Object read(VirtualFrame frame, byte[] source) {
int index = advanceSourcePositionNoThrow(frame);

if (index == -1) {
return getContext().getCoreLibrary().getNilObject();
}

return source[index];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.read;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.runtime.RubyContext;

import java.util.Arrays;

@NodeChildren({
@NodeChild(value = "source", type = SourceNode.class),
})
public abstract class ReadBytesNode extends PackNode {

private final int count;
private final boolean consumePartial;

public ReadBytesNode(RubyContext context, int count, boolean consumePartial) {
super(context);
this.count = count;
this.consumePartial = consumePartial;
}

@Specialization(guards = "isNull(source)")
public void read(VirtualFrame frame, Object source) {
CompilerDirectives.transferToInterpreter();

// Advance will handle the error
advanceSourcePosition(frame, count);

throw new IllegalStateException();
}

@Specialization
public Object read(VirtualFrame frame, byte[] source) {
int index = advanceSourcePositionNoThrow(frame, count, consumePartial);

if (index == -1) {
if (consumePartial) {
return MissingValue.INSTANCE;
} else {
return getContext().getCoreLibrary().getNilObject();
}
}

return Arrays.copyOfRange(source, index, index + count);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.read;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jcodings.Encoding;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.Pack;
import org.jruby.util.StringSupport;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@NodeChildren({
@NodeChild(value = "value", type = SourceNode.class),
})
public abstract class ReadHexStringNode extends PackNode {

private final ByteOrder byteOrder;
private final boolean star;
private final int length;

public ReadHexStringNode(RubyContext context, ByteOrder byteOrder, boolean star, int length) {
super(context);
this.byteOrder = byteOrder;
this.star = star;
this.length = length;
}

@Specialization
public Object read(VirtualFrame frame, byte[] source) {
// Bit string logic copied from jruby.util.Pack - see copyright and authorship there

final ByteBuffer encode = ByteBuffer.wrap(source, getSourcePosition(frame), getSourceLength(frame) - getSourcePosition(frame));

int occurrences = length;
byte[] lElem;

if (byteOrder == ByteOrder.BIG_ENDIAN) {
if (star || occurrences > encode.remaining() * 2) {
occurrences = encode.remaining() * 2;
}
int bits = 0;
lElem = new byte[occurrences];
for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) {
if ((lCurByte & 1) != 0) {
bits <<= 4;
} else {
bits = encode.get();
}
lElem[lCurByte] = Pack.sHexDigits[(bits >>> 4) & 15];
}
} else {
if (star || occurrences > encode.remaining() * 2) {
occurrences = encode.remaining() * 2;
}
int bits = 0;
lElem = new byte[occurrences];
for (int lCurByte = 0; lCurByte < occurrences; lCurByte++) {
if ((lCurByte & 1) != 0) {
bits >>>= 4;
} else {
bits = encode.get();
}
lElem[lCurByte] = Pack.sHexDigits[bits & 15];
}
}

final Encoding encoding = Encoding.load("ASCII");
final ByteList result = new ByteList(lElem, encoding, false);
setSourcePosition(frame, encode.position());

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), result, StringSupport.CR_UNKNOWN, null);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.read;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jcodings.Encoding;
import org.jruby.RubyString;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.Pack;
import org.jruby.util.StringSupport;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@NodeChildren({
@NodeChild(value = "value", type = SourceNode.class),
})
public abstract class ReadMIMEStringNode extends PackNode {

private final int length;

public ReadMIMEStringNode(RubyContext context, int length) {
super(context);
this.length = length;
}

@Specialization
public Object read(VirtualFrame frame, byte[] source) {
// Bit string logic copied from jruby.util.Pack - see copyright and authorship there

final ByteBuffer encode = ByteBuffer.wrap(source, getSourcePosition(frame), getSourceLength(frame) - getSourcePosition(frame));

int occurrences = length;

byte[] lElem = new byte[Math.max(encode.remaining(),0)];
int index = 0;
for(;;) {
if (!encode.hasRemaining()) break;
int c = Pack.safeGet(encode);
if (c != '=') {
lElem[index++] = (byte)c;
} else {
if (!encode.hasRemaining()) break;
encode.mark();
int c1 = Pack.safeGet(encode);
if (c1 == '\n' || (c1 == '\r' && (c1 = Pack.safeGet(encode)) == '\n')) continue;
int d1 = Character.digit(c1, 16);
if (d1 == -1) {
encode.reset();
break;
}
encode.mark();
if (!encode.hasRemaining()) break;
int c2 = Pack.safeGet(encode);
int d2 = Character.digit(c2, 16);
if (d2 == -1) {
encode.reset();
break;
}
byte value = (byte)(d1 << 4 | d2);
lElem[index++] = value;
}
}

final Encoding encoding = Encoding.load("ASCII");
final ByteList result = new ByteList(lElem, 0, index, encoding, false);
setSourcePosition(frame, encode.position());

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), result, StringSupport.CR_UNKNOWN, null);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.read;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.runtime.RubyContext;

import java.nio.ByteBuffer;

@NodeChildren({
@NodeChild(value = "source", type = SourceNode.class),
})
public abstract class ReadUTF8CharacterNode extends PackNode {

public ReadUTF8CharacterNode(RubyContext context) {
super(context);
}

@Specialization(guards = "isNull(source)")
public void read(VirtualFrame frame, Object source) {
CompilerDirectives.transferToInterpreter();

// Advance will handle the error
advanceSourcePosition(frame, 1);

throw new IllegalStateException();
}

@Specialization
public Object read(VirtualFrame frame, byte[] source) {
final int index = getSourcePosition(frame);
final int sourceLength = getSourceLength(frame);

if (index == -1) {
//return getContext().getCoreLibrary().getNilObject();
throw new UnsupportedOperationException();
}

if (index >= sourceLength) {
return MissingValue.INSTANCE;
}

long codepoint = source[index] & 0xff;
final int length;

if (codepoint >> 7 == 0) {
length = 1;
codepoint &= 0b01111111;
} else if (codepoint >> 5 == 0b00000110) {
length = 2;
codepoint &= 0b00011111;
} else if (codepoint >> 4 == 0b00001110) {
length = 3;
codepoint &= 0b00001111;
} else if (codepoint >> 3 == 0b00011110) {
length = 4;
codepoint &= 0b00000111;
} else if (codepoint >> 2 == 0b00111110) {
length = 5;
codepoint &= 0b00000011;
} else if (codepoint >> 1 == 0b01111110) {
length = 6;
codepoint &= 0b00000001;
} else {
// Not UTF-8, so just pass the first byte through
length = 1;
}

if (index + length > sourceLength) {
return MissingValue.INSTANCE;
}

for (int n = 1; n < length; n++) {
codepoint <<= 6;
codepoint |= source[index + n] & 0b00111111;
}

setSourcePosition(frame, index + length);

return codepoint;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.read;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jcodings.Encoding;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.nodes.SourceNode;
import org.jruby.truffle.format.runtime.exceptions.NoImplicitConversionException;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.util.ByteList;
import org.jruby.util.Pack;
import org.jruby.util.StringSupport;

import java.nio.ByteBuffer;

/**
* Read a string that contains UU-encoded data and write as actual binary
* data.
*/
@NodeChildren({
@NodeChild(value = "source", type = SourceNode.class),
})
public abstract class ReadUUStringNode extends PackNode {

private final int length;
private final boolean ignoreStar;

public ReadUUStringNode(RubyContext context, int length, boolean ignoreStar) {
super(context);
this.length = length;
this.ignoreStar = ignoreStar;
}

/*@Specialization
public Object write(long bytes) {
throw new NoImplicitConversionException(bytes, "String");
}
@Specialization(guards = "isEmpty(bytes)")
public Object writeEmpty(VirtualFrame frame, ByteList bytes) {
return null;
}
@Specialization(guards = "!isEmpty(bytes)")
public Object write(VirtualFrame frame, ByteList bytes) {
writeBytes(frame, encode(bytes));
return null;
}*/

@Specialization
protected Object encode(VirtualFrame frame, byte[] source) {
CompilerDirectives.transferToInterpreter();

// TODO CS 28-Dec-15 should write our own optimizable version of UU

/*
* Copied from JRuby's Pack class.
*
* **** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
* Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
* Copyright (C) 2003-2004 Thomas E Enebo <enebo@acm.org>
* Copyright (C) 2004 Charles O Nutter <headius@headius.com>
* Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
* Copyright (C) 2005 Derek Berner <derek.berner@state.nm.us>
* Copyright (C) 2006 Evan Buswell <ebuswell@gmail.com>
* Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
* Copyright (C) 2009 Joseph LaFata <joe@quibb.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/

final ByteBuffer encode = ByteBuffer.wrap(source, getSourcePosition(frame), getSourceLength(frame) - getSourcePosition(frame));

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); // Possible Checksum Byte
} else if (encode.hasRemaining()) {
encode.position(encode.position() - 1);
}
}
}

final Encoding encoding = Encoding.load("ASCII");
final ByteList result = new ByteList(lElem, 0, index, encoding, false);

setSourcePosition(frame, encode.position());

return Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), result, StringSupport.CR_UNKNOWN, null);
}

private static int safeGet(ByteBuffer encode) {
while (encode.hasRemaining()) {
int got = encode.get() & 0xff;

if (got != 0) return got;
}

return 0;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.type;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.nodes.core.FixnumOrBignumNode;
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.runtime.RubyContext;

import java.math.BigInteger;

@NodeChildren({
@NodeChild(value = "value", type = PackNode.class),
})
public abstract class AsUnsignedNode extends PackNode {

@Child private FixnumOrBignumNode fixnumOrBignumNode;

public AsUnsignedNode(RubyContext context) {
super(context);
}

@Specialization
public MissingValue asUnsigned(VirtualFrame frame, MissingValue missingValue) {
return missingValue;
}

@Specialization(guards = "isNil(nil)")
public DynamicObject asUnsigned(VirtualFrame frame, DynamicObject nil) {
return nil;
}

@Specialization
public int asUnsigned(VirtualFrame frame, short value) {
return value & 0xffff;
}

@Specialization
public long asUnsigned(VirtualFrame frame, int value) {
return value & 0xffffffffL;
}

@Specialization
public Object asUnsigned(VirtualFrame frame, long value) {
if (fixnumOrBignumNode == null) {
CompilerDirectives.transferToInterpreter();
fixnumOrBignumNode = insert(FixnumOrBignumNode.create(getContext(), getSourceSection()));
}

return fixnumOrBignumNode.fixnumOrBignum(asUnsigned(value));
}

private static final long UNSIGNED_LONG_MASK = 0x7fffffffffffffffL;

@CompilerDirectives.TruffleBoundary
private BigInteger asUnsigned(long value) {
BigInteger bigIntegerValue = BigInteger.valueOf(value & UNSIGNED_LONG_MASK);

if (value < 0) {
bigIntegerValue = bigIntegerValue.setBit(Long.SIZE - 1);
}

return bigIntegerValue;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.nodes.write;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.format.nodes.PackNode;
import org.jruby.truffle.format.runtime.MissingValue;
import org.jruby.truffle.runtime.RubyContext;

import java.util.Arrays;

@NodeChildren({
@NodeChild(value = "value", type = PackNode.class),
})
public abstract class WriteValueNode extends PackNode {

public WriteValueNode(RubyContext context) {
super(context);
}

@Specialization
public Object doWrite(VirtualFrame frame, MissingValue value) {
return null;
}

@Specialization(guards = "!isMissingValue(value)")
public Object doWrite(VirtualFrame frame, Object value) {
Object[] output = ensureCapacity(frame, 1);
final int outputPosition = getOutputPosition(frame);
output[outputPosition] = value;
setOutputPosition(frame, outputPosition + 1);
return null;
}

private Object[] ensureCapacity(VirtualFrame frame, int length) {
Object[] output = (Object[]) getOutput(frame);
final int outputPosition = getOutputPosition(frame);

if (outputPosition + length > output.length) {
// If we ran out of output byte[], deoptimize and next time we'll allocate more

CompilerDirectives.transferToInterpreterAndInvalidate();
output = Arrays.copyOf(output, (output.length + length) * 2);
setOutput(frame, output);
}

return output;
}

protected boolean isMissingValue(Object object) {
return object instanceof MissingValue;
}

}
Original file line number Diff line number Diff line change
@@ -80,7 +80,7 @@ public CallTarget compile(String format) {
* that pattern is repeated. If it is we have the loop. Nothing more
* complicated than that.
*/
private String recoverLoop(String format) {
public static String recoverLoop(String format) {
int break_point = 0;

while (break_point < format.length()) {
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
@@ -31,6 +31,9 @@
import java.util.Deque;
import java.util.List;

import org.jruby.truffle.format.parser.PackBaseListener;
import org.jruby.truffle.format.parser.PackParser;

public class PackTreeBuilder extends PackBaseListener {

private final RubyContext context;
@@ -57,68 +60,107 @@ public void exitSequence(PackParser.SequenceContext ctx) {
}

@Override
public void exitCharacter(PackParser.CharacterContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(8, ByteOrder.nativeOrder())));
public void exitInt8(PackParser.Int8Context ctx) {
integer(8, null, ctx.count());
}

@Override
public void exitUint8(PackParser.Uint8Context ctx) {
integer(8, null, ctx.count());
}

@Override
public void exitInt16Little(PackParser.Int16LittleContext ctx) {
integer(16, ByteOrder.LITTLE_ENDIAN, ctx.count());
}

@Override
public void exitInt16Big(PackParser.Int16BigContext ctx) {
integer(16, ByteOrder.BIG_ENDIAN, ctx.count());
}

@Override
public void exitInt16Native(PackParser.Int16NativeContext ctx) {
integer(16, ByteOrder.nativeOrder(), ctx.count());
}

@Override
public void exitShortLittle(PackParser.ShortLittleContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(16, ByteOrder.LITTLE_ENDIAN)));
public void exitUint16Little(PackParser.Uint16LittleContext ctx) {
integer(16, ByteOrder.LITTLE_ENDIAN, ctx.count());
}

@Override
public void exitShortBig(PackParser.ShortBigContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(16, ByteOrder.BIG_ENDIAN)));
public void exitUint16Big(PackParser.Uint16BigContext ctx) {
integer(16, ByteOrder.BIG_ENDIAN, ctx.count());
}

@Override
public void exitShortNative(PackParser.ShortNativeContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(16, ByteOrder.nativeOrder())));
public void exitUint16Native(PackParser.Uint16NativeContext ctx) {
integer(16, ByteOrder.nativeOrder(), ctx.count());
}

@Override
public void exitIntLittle(PackParser.IntLittleContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(32, ByteOrder.LITTLE_ENDIAN)));
public void exitInt32Little(PackParser.Int32LittleContext ctx) {
integer(32, ByteOrder.LITTLE_ENDIAN, ctx.count());
}

@Override
public void exitIntBig(PackParser.IntBigContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(32, ByteOrder.BIG_ENDIAN)));
public void exitInt32Big(PackParser.Int32BigContext ctx) {
integer(32, ByteOrder.BIG_ENDIAN, ctx.count());
}

@Override
public void exitIntNative(PackParser.IntNativeContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(32, ByteOrder.nativeOrder())));
public void exitInt32Native(PackParser.Int32NativeContext ctx) {
integer(32, ByteOrder.nativeOrder(), ctx.count());
}

@Override
public void exitLongLittle(PackParser.LongLittleContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(32, ByteOrder.LITTLE_ENDIAN)));
public void exitUint32Little(PackParser.Uint32LittleContext ctx) {
integer(32, ByteOrder.LITTLE_ENDIAN, ctx.count());
}

@Override
public void exitLongBig(PackParser.LongBigContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(32, ByteOrder.BIG_ENDIAN)));
public void exitUint32Big(PackParser.Uint32BigContext ctx) {
integer(32, ByteOrder.BIG_ENDIAN, ctx.count());
}

@Override
public void exitLongNative(PackParser.LongNativeContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(32, ByteOrder.nativeOrder())));
public void exitUint32Native(PackParser.Uint32NativeContext ctx) {
integer(32, ByteOrder.nativeOrder(), ctx.count());
}

@Override
public void exitQuadLittle(PackParser.QuadLittleContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(64, ByteOrder.LITTLE_ENDIAN)));
public void exitInt64Little(PackParser.Int64LittleContext ctx) {
integer(64, ByteOrder.LITTLE_ENDIAN, ctx.count());
}

@Override
public void exitQuadBig(PackParser.QuadBigContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(64, ByteOrder.BIG_ENDIAN)));
public void exitInt64Big(PackParser.Int64BigContext ctx) {
integer(64, ByteOrder.BIG_ENDIAN, ctx.count());
}

@Override
public void exitQuadNative(PackParser.QuadNativeContext ctx) {
appendNode(applyCount(ctx.count(), writeInteger(64, ByteOrder.nativeOrder())));
public void exitInt64Native(PackParser.Int64NativeContext ctx) {
integer(64, ByteOrder.nativeOrder(), ctx.count());
}

@Override
public void exitUint64Little(PackParser.Uint64LittleContext ctx) {
integer(64, ByteOrder.LITTLE_ENDIAN, ctx.count());
}

@Override
public void exitUint64Big(PackParser.Uint64BigContext ctx) {
integer(64, ByteOrder.BIG_ENDIAN, ctx.count());
}

@Override
public void exitUint64Native(PackParser.Uint64NativeContext ctx) {
integer(64, ByteOrder.nativeOrder(), ctx.count());
}

public void integer(int size, ByteOrder byteOrder, PackParser.CountContext count) {
appendNode(applyCount(count, writeInteger(size, byteOrder)));
}

@Override
@@ -138,15 +180,15 @@ public void exitBerInteger(PackParser.BerIntegerContext ctx) {
}

@Override
public void exitDoubleNative(PackParser.DoubleNativeContext ctx) {
public void exitF64Native(PackParser.F64NativeContext ctx) {
appendNode(applyCount(ctx.count(),
writeInteger(64, ByteOrder.nativeOrder(),
ReinterpretLongNodeGen.create(context,
ReadDoubleNodeGen.create(context, new SourceNode())))));
}

@Override
public void exitFloatNative(PackParser.FloatNativeContext ctx) {
public void exitF32Native(PackParser.F32NativeContext ctx) {
appendNode(applyCount(ctx.count(),
writeInteger(32, ByteOrder.nativeOrder(),
ReinterpretLongNodeGen.create(context,
@@ -155,15 +197,15 @@ public void exitFloatNative(PackParser.FloatNativeContext ctx) {
}

@Override
public void exitDoubleLittle(PackParser.DoubleLittleContext ctx) {
public void exitF64Little(PackParser.F64LittleContext ctx) {
appendNode(applyCount(ctx.count(),
writeInteger(64, ByteOrder.LITTLE_ENDIAN,
ReinterpretLongNodeGen.create(context,
ReadDoubleNodeGen.create(context, new SourceNode())))));
}

@Override
public void exitFloatLittle(PackParser.FloatLittleContext ctx) {
public void exitF32Little(PackParser.F32LittleContext ctx) {
appendNode(applyCount(ctx.count(),
writeInteger(32, ByteOrder.LITTLE_ENDIAN,
ReinterpretLongNodeGen.create(context,
@@ -172,15 +214,15 @@ public void exitFloatLittle(PackParser.FloatLittleContext ctx) {
}

@Override
public void exitDoubleBig(PackParser.DoubleBigContext ctx) {
public void exitF64Big(PackParser.F64BigContext ctx) {
appendNode(applyCount(ctx.count(),
writeInteger(64, ByteOrder.BIG_ENDIAN,
ReinterpretLongNodeGen.create(context,
ReadDoubleNodeGen.create(context, new SourceNode())))));
}

@Override
public void exitFloatBig(PackParser.FloatBigContext ctx) {
public void exitF32Big(PackParser.F32BigContext ctx) {
appendNode(applyCount(ctx.count(),
writeInteger(32, ByteOrder.BIG_ENDIAN,
ReinterpretLongNodeGen.create(context,
@@ -302,10 +344,12 @@ public void exitPointer(PackParser.PointerContext ctx) {
public void exitAt(PackParser.AtContext ctx) {
final int position;

if (ctx.INT() == null) {
if (ctx.count() == null) {
position = 1;
} else if (ctx.count() != null && ctx.count().INT() == null) {
throw new UnsupportedOperationException();
} else {
position = Integer.parseInt(ctx.INT().getText());
position = Integer.parseInt(ctx.count().INT().getText());
}

appendNode(new AtNode(context, position));
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.parser;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.jruby.truffle.format.nodes.PackRootNode;
import org.jruby.truffle.format.nodes.UnpackRootNode;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;

import org.jruby.truffle.format.parser.PackLexer;
import org.jruby.truffle.format.parser.PackParser;

public class UnpackCompiler {

private final RubyContext context;
private final RubyNode currentNode;

public UnpackCompiler(RubyContext context, RubyNode currentNode) {
this.context = context;
this.currentNode = currentNode;
}

public CallTarget compile(String format) {
if (format.length() > 32) {
format = PackCompiler.recoverLoop(format);
}

final PackErrorListener errorListener = new PackErrorListener(context, currentNode);

final ANTLRInputStream input = new ANTLRInputStream(format);

final PackLexer lexer = new PackLexer(input);
lexer.removeErrorListeners();
lexer.addErrorListener(errorListener);

final CommonTokenStream tokens = new CommonTokenStream(lexer);

final PackParser parser = new PackParser(tokens);

final UnpackTreeBuilder builder = new UnpackTreeBuilder(context, currentNode);
parser.addParseListener(builder);

parser.removeErrorListeners();
parser.addErrorListener(errorListener);

parser.sequence();

return Truffle.getRuntime().createCallTarget(
new UnpackRootNode(PackCompiler.describe(format), builder.getEncoding(), builder.getNode()));
}

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.format.runtime;

public class MissingValue {

public static MissingValue INSTANCE = new MissingValue();

private MissingValue() {
}

}
Original file line number Diff line number Diff line change
@@ -11,19 +11,19 @@

public class PackResult {

private final byte[] output;
private final Object output;
private final int outputLength;
private final boolean tainted;
private final PackEncoding encoding;

public PackResult(byte[] output, int outputLength, boolean tainted, PackEncoding encoding) {
public PackResult(Object output, int outputLength, boolean tainted, PackEncoding encoding) {
this.output = output;
this.outputLength = outputLength;
this.tainted = tainted;
this.encoding = encoding;
}

public byte[] getOutput() {
public Object getOutput() {
return output;
}

Original file line number Diff line number Diff line change
@@ -1941,7 +1941,7 @@ private RuntimeException handleException(PackException exception) {
}

private DynamicObject finishFormat(int formatLength, PackResult result) {
final DynamicObject string = createString(new ByteList(result.getOutput(), 0, result.getOutputLength(), false));
final DynamicObject string = createString(new ByteList((byte[]) result.getOutput(), 0, result.getOutputLength()));

if (formatLength == 0) {
StringOperations.forceEncoding(string, USASCIIEncoding.INSTANCE);
148 changes: 139 additions & 9 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java
Original file line number Diff line number Diff line change
@@ -24,10 +24,13 @@
*/
package org.jruby.truffle.nodes.core;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.*;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.utilities.BranchProfile;
import com.oracle.truffle.api.utilities.ConditionProfile;
@@ -38,8 +41,13 @@
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.format.parser.PackCompiler;
import org.jruby.truffle.format.parser.UnpackCompiler;
import org.jruby.truffle.format.runtime.PackResult;
import org.jruby.truffle.format.runtime.exceptions.*;
import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.StringCachingGuards;
import org.jruby.truffle.nodes.cast.CmpIntNode;
import org.jruby.truffle.nodes.cast.CmpIntNodeGen;
import org.jruby.truffle.nodes.cast.TaintResultNode;
@@ -51,10 +59,7 @@
import org.jruby.truffle.nodes.core.fixnum.FixnumLowerNodeGen;
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.nodes.objects.AllocateObjectNode;
import org.jruby.truffle.nodes.objects.AllocateObjectNodeGen;
import org.jruby.truffle.nodes.objects.IsFrozenNode;
import org.jruby.truffle.nodes.objects.IsFrozenNodeGen;
import org.jruby.truffle.nodes.objects.*;
import org.jruby.truffle.nodes.rubinius.ByteArrayNodes;
import org.jruby.truffle.nodes.rubinius.StringPrimitiveNodes;
import org.jruby.truffle.nodes.rubinius.StringPrimitiveNodesFactory;
@@ -2145,18 +2150,143 @@ public Object trSBang(VirtualFrame frame, DynamicObject self, DynamicObject from
}
}

@CoreMethod(names = "unpack", required = 1)
@CoreMethod(names = "unpack", required = 1, taintFromParameter = 0)
@ImportStatic(StringCachingGuards.class)
public abstract static class UnpackNode extends ArrayCoreMethodNode {

@Child private TaintNode taintNode;

public UnpackNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = {"isRubyString(format)", "byteListsEqual(format, cachedFormat)"}, limit = "getCacheLimit()")
public DynamicObject unpackCached(
VirtualFrame frame,
DynamicObject string,
DynamicObject format,
@Cached("privatizeByteList(format)") ByteList cachedFormat,
@Cached("create(compileFormat(format))") DirectCallNode callUnpackNode) {
final ByteList bytes = Layouts.STRING.getByteList(string);

final PackResult result;

try {
// TODO CS 20-Dec-15 bytes() creates a copy as the nodes aren't ready for a start offset yet
result = (PackResult) callUnpackNode.call(frame, new Object[]{bytes.bytes(), bytes.length()});
} catch (PackException e) {
CompilerDirectives.transferToInterpreter();
throw handleException(e);
}

return finishUnpack(cachedFormat, result);
}

@Specialization(contains = "unpackCached", guards = "isRubyString(format)")
public DynamicObject unpackUncached(
VirtualFrame frame,
DynamicObject string,
DynamicObject format,
@Cached("create()") IndirectCallNode callUnpackNode) {
final ByteList bytes = Layouts.STRING.getByteList(string);

final PackResult result;

try {
// TODO CS 20-Dec-15 bytes() creates a copy as the nodes aren't ready for a start offset yet
result = (PackResult) callUnpackNode.call(frame, compileFormat(format), new Object[]{bytes.bytes(), bytes.length()});
} catch (PackException e) {
CompilerDirectives.transferToInterpreter();
throw handleException(e);
}

return finishUnpack(StringOperations.getByteList(format), result);
}

private RuntimeException handleException(PackException exception) {
try {
throw exception;
} catch (TooFewArgumentsException e) {
return new RaiseException(getContext().getCoreLibrary().argumentError("too few arguments", this));
} catch (NoImplicitConversionException e) {
return new RaiseException(getContext().getCoreLibrary().typeErrorNoImplicitConversion(e.getObject(), e.getTarget(), this));
} catch (OutsideOfStringException e) {
return new RaiseException(getContext().getCoreLibrary().argumentError("X outside of string", this));
} catch (CantCompressNegativeException e) {
return new RaiseException(getContext().getCoreLibrary().argumentError("can't compress negative numbers", this));
} catch (RangeException e) {
return new RaiseException(getContext().getCoreLibrary().rangeError(e.getMessage(), this));
} catch (CantConvertException e) {
return new RaiseException(getContext().getCoreLibrary().typeError(e.getMessage(), this));
}
}

private DynamicObject finishUnpack(ByteList format, PackResult result) {
final DynamicObject array = Layouts.ARRAY.createArray(getContext().getCoreLibrary().getArrayFactory(), result.getOutput(), result.getOutputLength());

if (format.length() == 0) {
//StringOperations.forceEncoding(string, USASCIIEncoding.INSTANCE);
} else {
switch (result.getEncoding()) {
case DEFAULT:
case ASCII_8BIT:
break;
case US_ASCII:
//StringOperations.forceEncoding(string, USASCIIEncoding.INSTANCE);
break;
case UTF_8:
//StringOperations.forceEncoding(string, UTF8Encoding.INSTANCE);
break;
default:
throw new UnsupportedOperationException();
}
}

if (result.isTainted()) {
if (taintNode == null) {
CompilerDirectives.transferToInterpreter();
taintNode = insert(TaintNodeGen.create(getContext(), getEncapsulatingSourceSection(), null));
}

taintNode.executeTaint(array);
}

return array;
}

@Specialization
public Object unpack(VirtualFrame frame, DynamicObject array, boolean format) {
return ruby(frame, "raise TypeError");
}

@Specialization
public Object unpack(VirtualFrame frame, DynamicObject array, int format) {
return ruby(frame, "raise TypeError");
}

@Specialization
public Object unpack(VirtualFrame frame, DynamicObject array, long format) {
return ruby(frame, "raise TypeError");
}

@Specialization(guards = "isNil(format)")
public Object unpackNil(VirtualFrame frame, DynamicObject array, Object format) {
return ruby(frame, "raise TypeError");
}

@Specialization(guards = {"!isRubyString(format)", "!isBoolean(format)", "!isInteger(format)", "!isLong(format)", "!isNil(format)"})
public Object unpack(VirtualFrame frame, DynamicObject array, Object format) {
return ruby(frame, "unpack(format.to_str)", "format", format);
}

@TruffleBoundary
@Specialization(guards = "isRubyString(format)")
public DynamicObject unpack(DynamicObject string, DynamicObject format) {
final org.jruby.RubyArray jrubyArray = Pack.unpack(getContext().getRuntime(), StringOperations.getByteList(string), StringOperations.getByteList(format));
return getContext().toTruffle(jrubyArray);
protected CallTarget compileFormat(DynamicObject format) {
assert RubyGuards.isRubyString(format);
return new UnpackCompiler(getContext(), this).compile(format.toString());
}

protected int getCacheLimit() {
return getContext().getOptions().UNPACK_CACHE;
}

}
Original file line number Diff line number Diff line change
@@ -2424,7 +2424,7 @@ private RuntimeException handleException(PackException exception) {
}

private DynamicObject finishPack(int formatLength, PackResult result) {
final DynamicObject string = createString(new ByteList(result.getOutput(), 0, result.getOutputLength(), false));
final DynamicObject string = createString(new ByteList((byte[]) result.getOutput(), 0, result.getOutputLength()));

if (formatLength == 0) {
StringOperations.forceEncoding(string, USASCIIEncoding.INSTANCE);
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@ public class Options {
public final int SYMBOL_TO_PROC_CACHE = org.jruby.util.cli.Options.TRUFFLE_SYMBOL_TO_PROC_CACHE.load();
public final int ALLOCATE_CLASS_CACHE = org.jruby.util.cli.Options.TRUFFLE_ALLOCATE_CLASS_CACHE.load();
public final int PACK_CACHE = org.jruby.util.cli.Options.TRUFFLE_PACK_CACHE.load();
public final int UNPACK_CACHE = org.jruby.util.cli.Options.TRUFFLE_UNPACK_CACHE.load();
public final int EVAL_CACHE = org.jruby.util.cli.Options.TRUFFLE_EVAL_CACHE.load();
public final int ENCODING_COMPATIBILE_QUERY_CACHE = org.jruby.util.cli.Options.ENCODING_COMPATIBLE_QUERY_CACHE.load();

Original file line number Diff line number Diff line change
@@ -359,6 +359,10 @@ public static long[] longCopyOf(int[] ints, int newLength) {
private static final int INITIAL_CAPACITY = 16;

public static int capacity(int current, int needed) {
if (needed == 0) {
return 0;
}

assert current < needed;

if (needed < INITIAL_CAPACITY) {

0 comments on commit 663d163

Please sign in to comment.