Skip to content

Commit 6a8044d

Browse files
committed
Emulate CRuby and clean up append_as_bytes
* Calculate growth size in an initial loop. * Add equivalent to str_ensure_available_capa for growing strings. * Refactor for arity-split (just 0 and 1 arg forms added).
1 parent d1d6fcb commit 6a8044d

File tree

1 file changed

+55
-17
lines changed

1 file changed

+55
-17
lines changed

core/src/main/java/org/jruby/RubyString.java

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,24 @@ public final void modifyExpand(int length) {
10461046
clearCodeRange();
10471047
}
10481048

1049+
/**
1050+
* Ensure the backing store belongs to this string and has enough space to add extraLength bytes.
1051+
*
1052+
* MRI: str_ensure_available_capa
1053+
*
1054+
* @param context the current thread context
1055+
* @param extraLength the extra length needed
1056+
*/
1057+
public void ensureAvailable(ThreadContext context, int extraLength) {
1058+
int realSize = value.getRealSize();
1059+
1060+
if (realSize > Integer.MAX_VALUE - extraLength) {
1061+
throw argumentError(context, "string sizes too big");
1062+
}
1063+
1064+
modifyExpand(realSize + extraLength);
1065+
}
1066+
10491067
// io_set_read_length
10501068
public void setReadLength(int length) {
10511069
if (size() != length) {
@@ -5514,30 +5532,50 @@ private IRubyObject rpartitionMismatch(ThreadContext context) {
55145532

55155533
@JRubyMethod(rest = true)
55165534
public IRubyObject append_as_bytes(ThreadContext context, IRubyObject[] args) {
5517-
checkFrozen();
5518-
modify();
5535+
int neededCapacity = 0;
5536+
for (var arg : args) neededCapacity += byteCapacityFor(context, arg);
5537+
this.ensureAvailable(context, neededCapacity);
5538+
for (var arg : args) appendBytes(context, arg);
55195539

5520-
int length = args.length;
5521-
for (int i = 0; i < length; i++) {
5522-
var arg = args[i];
5540+
return this;
5541+
}
55235542

5524-
if (arg instanceof RubyFixnum fix) {
5525-
cat(fix.getIntValue() & 0xff);
5526-
} else if (arg instanceof RubyBignum big) {
5527-
cat(big.getIntValue() & 0xff);
5528-
} else if (arg instanceof RubyString str) {
5529-
value.append(str.getByteList());
5530-
} else {
5531-
throw typeError(context, str(context.runtime, "wrong argument type ", types(context.runtime, arg.getType()), " (expected String or Integer)"));
5532-
}
5533-
}
5543+
@JRubyMethod
5544+
public IRubyObject append_as_bytes(ThreadContext context) {
5545+
this.ensureAvailable(context, 0);
55345546

5535-
// FIXME: MRI tries and minimize pain by trying to figure out if it should change cr and this is a quick hammer.
5536-
clearCodeRange();
5547+
return this;
5548+
}
5549+
5550+
@JRubyMethod
5551+
public IRubyObject append_as_bytes(ThreadContext context, IRubyObject arg0) {
5552+
ensureAvailable(context, byteCapacityFor(context, arg0));
5553+
appendBytes(context, arg0);
55375554

55385555
return this;
55395556
}
55405557

5558+
private static int byteCapacityFor(ThreadContext context, IRubyObject arg) {
5559+
return switch (arg) {
5560+
case RubyInteger ignored -> 1;
5561+
case RubyString str -> str.getByteList().realSize();
5562+
default ->
5563+
throw typeError(context, str(context.runtime, "wrong argument type ", types(context.runtime, arg.getType()), " (expected String or Integer)"));
5564+
};
5565+
}
5566+
5567+
private void appendBytes(ThreadContext context, IRubyObject arg) {
5568+
if (arg instanceof RubyFixnum fix) {
5569+
cat(fix.getIntValue() & 0xff);
5570+
} else if (arg instanceof RubyBignum big) {
5571+
cat(big.getBigIntegerValue().intValue() & 0xff);
5572+
} else if (arg instanceof RubyString str) {
5573+
value.append(str.getByteList());
5574+
} else {
5575+
throw runtimeError(context, "BUG: append_as_bytes arguments should have been validated");
5576+
}
5577+
}
5578+
55415579
/** rb_str_chop / rb_str_chop_bang
55425580
*
55435581
*/

0 commit comments

Comments
 (0)