57
57
import org .truffleruby .core .encoding .RubyEncoding ;
58
58
import org .truffleruby .core .format .BytesResult ;
59
59
import org .truffleruby .core .format .FormatExceptionTranslator ;
60
+ import org .truffleruby .core .format .FormatRootNode ;
60
61
import org .truffleruby .core .format .exceptions .FormatException ;
61
62
import org .truffleruby .core .format .pack .PackCompiler ;
62
63
import org .truffleruby .core .hash .HashingNodes ;
@@ -1491,15 +1492,15 @@ public void accept(Node node, CallBlockNode yieldNode, RubyArray array, Object s
1491
1492
1492
1493
}
1493
1494
1494
- @ CoreMethod ( names = "pack " , required = 1 , split = Split . ALWAYS )
1495
- public abstract static class ArrayPackNode extends CoreMethodArrayArgumentsNode {
1495
+ @ Primitive ( name = "array_pack " , lowerFixnum = 1 )
1496
+ public abstract static class ArrayPackPrimitiveNode extends PrimitiveArrayArgumentsNode {
1496
1497
1497
1498
@ Specialization
1498
- RubyString pack (RubyArray array , Object format ,
1499
+ RubyString pack (RubyArray array , Object format , Object buffer ,
1499
1500
@ Cached ToStrNode toStrNode ,
1500
1501
@ Cached PackNode packNode ) {
1501
1502
final var formatAsString = toStrNode .execute (this , format );
1502
- return packNode .execute (this , array , formatAsString );
1503
+ return packNode .execute (this , array , formatAsString , buffer );
1503
1504
}
1504
1505
}
1505
1506
@@ -1508,28 +1509,35 @@ RubyString pack(RubyArray array, Object format,
1508
1509
@ ReportPolymorphism
1509
1510
public abstract static class PackNode extends RubyBaseNode {
1510
1511
1511
- public abstract RubyString execute (Node node , RubyArray array , Object format );
1512
+ public abstract RubyString execute (Node node , RubyArray array , Object format , Object buffer );
1512
1513
1513
1514
@ Specialization (
1514
1515
guards = {
1515
1516
"libFormat.isRubyString(format)" ,
1517
+ "libBuffer.isRubyString(buffer)" ,
1516
1518
"equalNode.execute(libFormat, format, cachedFormat, cachedEncoding)" },
1517
1519
limit = "getCacheLimit()" )
1518
- static RubyString packCached (Node node , RubyArray array , Object format ,
1520
+ static RubyString packCached (Node node , RubyArray array , Object format , Object buffer ,
1519
1521
@ Cached @ Shared InlinedBranchProfile exceptionProfile ,
1520
1522
@ Cached @ Shared InlinedConditionProfile resizeProfile ,
1521
1523
@ Cached @ Shared RubyStringLibrary libFormat ,
1524
+ @ Cached @ Shared RubyStringLibrary libBuffer ,
1522
1525
@ Cached @ Shared WriteObjectFieldNode writeAssociatedNode ,
1523
1526
@ Cached @ Shared TruffleString .FromByteArrayNode fromByteArrayNode ,
1527
+ @ Cached @ Shared TruffleString .CopyToByteArrayNode copyToByteArrayNode ,
1524
1528
@ Cached ("asTruffleStringUncached(format)" ) TruffleString cachedFormat ,
1525
1529
@ Cached ("libFormat.getEncoding(format)" ) RubyEncoding cachedEncoding ,
1526
1530
@ Cached ("cachedFormat.byteLength(cachedEncoding.tencoding)" ) int cachedFormatLength ,
1527
- @ Cached ("create(compileFormat(node, getJavaString(format)))" ) DirectCallNode callPackNode ,
1531
+ @ Cached ("compileFormat(node, getJavaString(format))" ) RootCallTarget formatCallTarget ,
1532
+ @ Cached ("create(formatCallTarget)" ) DirectCallNode callPackNode ,
1528
1533
@ Cached StringHelperNodes .EqualNode equalNode ) {
1534
+ final byte [] bytes = initOutputBytes (buffer , libBuffer , formatCallTarget , copyToByteArrayNode );
1535
+
1529
1536
final BytesResult result ;
1537
+
1530
1538
try {
1531
1539
result = (BytesResult ) callPackNode .call (
1532
- new Object []{ array .getStore (), array .size , false , null });
1540
+ new Object []{ array .getStore (), array .size , bytes , libBuffer . byteLength ( buffer ) });
1533
1541
} catch (FormatException e ) {
1534
1542
exceptionProfile .enter (node );
1535
1543
throw FormatExceptionTranslator .translate (getContext (node ), node , e );
@@ -1538,22 +1546,28 @@ static RubyString packCached(Node node, RubyArray array, Object format,
1538
1546
return finishPack (node , cachedFormatLength , result , resizeProfile , writeAssociatedNode , fromByteArrayNode );
1539
1547
}
1540
1548
1541
- @ Specialization (guards = { "libFormat.isRubyString(format)" }, replaces = "packCached" )
1542
- static RubyString packUncached (Node node , RubyArray array , Object format ,
1549
+ @ Specialization (guards = { "libFormat.isRubyString(format)" , "libBuffer.isRubyString(buffer)" },
1550
+ replaces = "packCached" )
1551
+ static RubyString packUncached (Node node , RubyArray array , Object format , Object buffer ,
1543
1552
@ Cached @ Shared InlinedBranchProfile exceptionProfile ,
1544
1553
@ Cached @ Shared InlinedConditionProfile resizeProfile ,
1545
1554
@ Cached @ Shared RubyStringLibrary libFormat ,
1555
+ @ Cached @ Shared RubyStringLibrary libBuffer ,
1546
1556
@ Cached @ Shared WriteObjectFieldNode writeAssociatedNode ,
1547
1557
@ Cached @ Shared TruffleString .FromByteArrayNode fromByteArrayNode ,
1558
+ @ Cached @ Shared TruffleString .CopyToByteArrayNode copyToByteArrayNode ,
1548
1559
@ Cached ToJavaStringNode toJavaStringNode ,
1549
1560
@ Cached IndirectCallNode callPackNode ) {
1550
1561
final String formatString = toJavaStringNode .execute (node , format );
1562
+ final RootCallTarget formatCallTarget = compileFormat (node , formatString );
1563
+ final byte [] bytes = initOutputBytes (buffer , libBuffer , formatCallTarget , copyToByteArrayNode );
1551
1564
1552
1565
final BytesResult result ;
1566
+
1553
1567
try {
1554
1568
result = (BytesResult ) callPackNode .call (
1555
- compileFormat ( node , formatString ) ,
1556
- new Object []{ array .getStore (), array .size , false , null });
1569
+ formatCallTarget ,
1570
+ new Object []{ array .getStore (), array .size , bytes , libBuffer . byteLength ( buffer ) });
1557
1571
} catch (FormatException e ) {
1558
1572
exceptionProfile .enter (node );
1559
1573
throw FormatExceptionTranslator .translate (getContext (node ), node , e );
@@ -1591,6 +1605,20 @@ protected static RootCallTarget compileFormat(Node node, String format) {
1591
1605
}
1592
1606
}
1593
1607
1608
+ private static byte [] initOutputBytes (Object buffer , RubyStringLibrary libBuffer ,
1609
+ RootCallTarget formatCallTarget , TruffleString .CopyToByteArrayNode copyToByteArrayNode ) {
1610
+ int bufferLength = libBuffer .byteLength (buffer );
1611
+ var formatRootNode = (FormatRootNode ) formatCallTarget .getRootNode ();
1612
+ int expectedLength = formatRootNode .getExpectedLength ();
1613
+
1614
+ // output buffer should be at least expectedLength to not mess up the expectedLength's logic
1615
+ final int length = Math .max (bufferLength , expectedLength );
1616
+ final byte [] bytes = new byte [length ];
1617
+ copyToByteArrayNode .execute (libBuffer .getTString (buffer ), 0 , bytes , 0 , bufferLength ,
1618
+ libBuffer .getTEncoding (buffer ));
1619
+ return bytes ;
1620
+ }
1621
+
1594
1622
protected int getCacheLimit () {
1595
1623
return getLanguage ().options .PACK_CACHE ;
1596
1624
}
0 commit comments