Skip to content

Commit 621369c

Browse files
committed
Arity-split String#encode!
This led to a few other things getting split, plus a lambda form that avoids the temporary string carrier array.
1 parent 1fd1f73 commit 621369c

File tree

2 files changed

+201
-95
lines changed

2 files changed

+201
-95
lines changed

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

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6596,39 +6596,43 @@ public IRubyObject encoding(ThreadContext context) {
65966596
return context.runtime.getEncodingService().getEncoding(value.getEncoding());
65976597
}
65986598

6599-
// TODO: re-split this
6600-
public IRubyObject encode_bang(ThreadContext context, IRubyObject arg0) {
6601-
return encode_bang(context, new IRubyObject[]{arg0});
6602-
}
6599+
@JRubyMethod(name = "encode!")
6600+
public IRubyObject encode_bang(ThreadContext context) {
6601+
modify19();
66036602

6604-
public IRubyObject encode_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
6605-
return encode_bang(context, new IRubyObject[]{arg0,arg1});
6603+
return EncodingUtils.strTranscode(context, this, RubyString::updateFromTranscode);
66066604
}
66076605

6608-
public IRubyObject encode_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
6609-
return encode_bang(context, new IRubyObject[]{arg0,arg1,arg2});
6606+
@JRubyMethod(name = "encode!")
6607+
public IRubyObject encode_bang(ThreadContext context, IRubyObject arg0) {
6608+
modify19();
6609+
6610+
return EncodingUtils.strTranscode(context, arg0, this, RubyString::updateFromTranscode);
66106611
}
66116612

6612-
@JRubyMethod(name = "encode!", optional = 3, checkArity = false)
6613-
public IRubyObject encode_bang(ThreadContext context, IRubyObject[] args) {
6614-
Arity.checkArgumentCount(context, args, 0, 3);
6613+
@JRubyMethod(name = "encode!")
6614+
public IRubyObject encode_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
6615+
modify19();
66156616

6616-
IRubyObject[] newstr_p;
6617-
Encoding encindex;
6617+
return EncodingUtils.strTranscode(context, arg0, arg1, this, RubyString::updateFromTranscode);
6618+
}
66186619

6620+
@JRubyMethod(name = "encode!")
6621+
public IRubyObject encode_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
66196622
modify19();
66206623

6621-
newstr_p = new IRubyObject[]{this};
6622-
encindex = EncodingUtils.strTranscode(context, args, newstr_p);
6624+
return EncodingUtils.strTranscode(context, arg0, arg1, arg2, this, RubyString::updateFromTranscode);
6625+
}
66236626

6624-
if (encindex == null) return this;
6625-
if (newstr_p[0] == this) {
6626-
setEncoding(encindex);
6627-
return this;
6627+
private static RubyString updateFromTranscode(ThreadContext context, RubyString self, Encoding encindex, RubyString newstr) {
6628+
if (encindex == null) return self;
6629+
if (newstr == self) {
6630+
self.setEncoding(encindex);
6631+
return self;
66286632
}
6629-
replace(newstr_p[0]);
6630-
setEncoding(encindex);
6631-
return this;
6633+
self.replace(newstr);
6634+
self.setEncoding(encindex);
6635+
return self;
66326636
}
66336637

66346638
@JRubyMethod
@@ -7244,4 +7248,13 @@ public RubyArray unpack(IRubyObject obj) {
72447248
return Pack.unpack(getRuntime(), this.value, stringValue(obj).value);
72457249
}
72467250

7251+
@Deprecated
7252+
public IRubyObject encode_bang(ThreadContext context, IRubyObject[] args) {
7253+
Arity.checkArgumentCount(context, args, 0, 2);
7254+
7255+
modify19();
7256+
7257+
return EncodingUtils.strTranscode(context, args, this, RubyString::updateFromTranscode);
7258+
}
7259+
72477260
}

core/src/main/java/org/jruby/util/io/EncodingUtils.java

Lines changed: 166 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -870,26 +870,26 @@ public static Encoding toEncodingIndex(ThreadContext context, IRubyObject enc) {
870870
}
871871

872872
// encoded_dup
873-
public static IRubyObject encodedDup(ThreadContext context, IRubyObject newstr, IRubyObject str, Encoding encindex) {
874-
if (encindex == null) return str.dup();
873+
public static RubyString encodedDup(ThreadContext context, RubyString str, Encoding encindex, RubyString newstr) {
874+
if (encindex == null) return (RubyString) str.dup();
875875
if (newstr == str) {
876-
newstr = str.dup();
876+
newstr = (RubyString) str.dup();
877877
} else {
878878
// set to same superclass
879-
((RubyBasicObject)newstr).setMetaClass(str.getMetaClass());
879+
newstr.setMetaClass(str.getMetaClass());
880880
}
881-
((RubyString)newstr).modify19();
882-
return strEncodeAssociate(context, newstr, encindex);
881+
newstr.modify19();
882+
return strEncodeAssociate(newstr, encindex);
883883
}
884884

885885
// str_encode_associate
886-
public static IRubyObject strEncodeAssociate(ThreadContext context, IRubyObject str, Encoding encidx) {
886+
public static RubyString strEncodeAssociate(RubyString str, Encoding encidx) {
887887
encAssociateIndex(str, encidx);
888888

889889
if (encAsciicompat(encidx)) {
890-
((RubyString)str).scanForCodeRange();
890+
str.scanForCodeRange();
891891
} else {
892-
((RubyString)str).setCodeRange(StringSupport.CR_VALID);
892+
str.setCodeRange(StringSupport.CR_VALID);
893893
}
894894

895895
return str;
@@ -911,21 +911,25 @@ public static IRubyObject encAssociateIndex(IRubyObject obj, Encoding encidx) {
911911
}
912912

913913
// str_encode
914-
public static IRubyObject strEncode(ThreadContext context, IRubyObject str, IRubyObject... args) {
915-
IRubyObject[] newstr_p = {str};
914+
public static IRubyObject strEncode(ThreadContext context, RubyString str) {
915+
return strTranscode(context, str, EncodingUtils::encodedDup);
916+
}
917+
918+
public static IRubyObject strEncode(ThreadContext context, RubyString str, IRubyObject toEncoding) {
919+
return strTranscode(context, toEncoding, str, EncodingUtils::encodedDup);
920+
}
916921

917-
Encoding dencindex = strTranscode(context, args, newstr_p);
922+
public static IRubyObject strEncode(ThreadContext context, RubyString str, IRubyObject toEncoding, IRubyObject forcedEncoding) {
923+
return strTranscode(context, toEncoding, forcedEncoding, str, EncodingUtils::encodedDup);
924+
}
918925

919-
return encodedDup(context, newstr_p[0], str, dencindex);
926+
public static IRubyObject strEncode(ThreadContext context, RubyString str, IRubyObject toEncoding, IRubyObject forcedEncoding, IRubyObject opts) {
927+
return strTranscode(context, toEncoding, forcedEncoding, opts, str, EncodingUtils::encodedDup);
920928
}
921929

922930
// rb_str_encode
923931
public static IRubyObject rbStrEncode(ThreadContext context, IRubyObject str, IRubyObject to, int ecflags, IRubyObject ecopt) {
924-
IRubyObject[] newstr_p = {str};
925-
926-
Encoding dencindex = strTranscode0(context, 1, new IRubyObject[]{to}, newstr_p, ecflags, ecopt);
927-
928-
return encodedDup(context, newstr_p[0], str, dencindex);
932+
return strTranscode1(context, to, (RubyString) str, ecflags, ecopt, EncodingUtils::encodedDup);
929933
}
930934

931935
// rb_str_encode
@@ -973,76 +977,107 @@ protected static boolean noDecorators(int ecflags) {
973977
}
974978

975979
// str_transcode
976-
public static Encoding strTranscode(ThreadContext context, IRubyObject[] args, IRubyObject[] self_p) {
977-
int ecflags = 0;
978-
int argc = args.length;
979-
IRubyObject[] ecopts_p = {context.nil};
980980

981-
if (args.length >= 1) {
982-
IRubyObject tmp = TypeConverter.checkHashType(context.runtime, args[args.length - 1]);
983-
if (!tmp.isNil()) {
984-
argc--;
985-
ecflags = econvPrepareOpts(context, tmp, ecopts_p);
986-
}
981+
public interface TranscodeResult {
982+
RubyString apply(ThreadContext context, RubyString str, Encoding enc, RubyString newStr);
983+
}
984+
985+
public static IRubyObject strTranscode(ThreadContext context, RubyString str, TranscodeResult result) {
986+
return strTranscode0(context, str, 0, context.nil, result);
987+
}
988+
989+
public static IRubyObject strTranscode(ThreadContext context, IRubyObject toEncoding, RubyString str, TranscodeResult result) {
990+
return strTranscode1(context, toEncoding, str, 0, context.nil, result);
991+
}
992+
993+
public static IRubyObject strTranscode(ThreadContext context, IRubyObject toEncoding, IRubyObject forcedEncoding, RubyString str, TranscodeResult result) {
994+
return strTranscode2(context, toEncoding, forcedEncoding, str, 0, context.nil, result);
995+
}
996+
997+
public static IRubyObject strTranscode(ThreadContext context, IRubyObject toEncoding, IRubyObject forcedEncoding, IRubyObject opts, RubyString str, TranscodeResult result) {
998+
IRubyObject tmp = TypeConverter.checkHashType(context.runtime, opts);
999+
if (tmp.isNil()) {
1000+
throw context.runtime.newArgumentError(3, 0, 2);
9871001
}
9881002

989-
return strTranscode0(context, argc, args, self_p, ecflags, ecopts_p[0]);
1003+
IRubyObject[] ecopts_p = {context.nil};
1004+
int ecflags = econvPrepareOpts(context, tmp, ecopts_p);
1005+
return strTranscode2(context, toEncoding, forcedEncoding, str, ecflags, ecopts_p[0], result);
9901006
}
9911007

992-
// str_transcode0
993-
public static Encoding strTranscode0(ThreadContext context, int argc, IRubyObject[] args, IRubyObject[] self_p, int ecflags, IRubyObject ecopts) {
994-
Ruby runtime = context.runtime;
1008+
private static IRubyObject strTranscode0(ThreadContext context, RubyString str, int ecflags, IRubyObject ecopts, TranscodeResult result) {
1009+
IRubyObject toEncoding = context.runtime.getEncodingService().getDefaultInternal();
1010+
if (toEncoding == null || toEncoding.isNil()) {
1011+
if (ecflags == 0) return result.apply(context, str, null, str);
1012+
toEncoding = objEncoding(context, str);
1013+
}
9951014

996-
IRubyObject str = self_p[0];
997-
IRubyObject arg1, arg2;
998-
Encoding[] senc_p = {null}, denc_p = {null};
999-
byte[][] sname_p = {null}, dname_p = {null};
1000-
Encoding dencindex;
10011015
boolean explicitlyInvalidReplace = true;
1016+
if ((ecflags & EConvFlags.INVALID_MASK) == 0) {
1017+
explicitlyInvalidReplace = false;
1018+
}
1019+
1020+
ecflags |= EConvFlags.INVALID_REPLACE | EConvFlags.UNDEF_REPLACE;
1021+
1022+
return strTranscode(context, toEncoding, context.nil, str, ecflags, ecopts, result, explicitlyInvalidReplace);
1023+
}
10021024

1003-
if (argc > 2) {
1004-
throw context.runtime.newArgumentError(args.length, 2);
1025+
private static IRubyObject strTranscode1(ThreadContext context, IRubyObject toEncoding, RubyString str, int ecflags, IRubyObject ecopts, TranscodeResult result) {
1026+
IRubyObject tmp = TypeConverter.checkHashType(context.runtime, toEncoding);
1027+
if (!tmp.isNil()) {
1028+
IRubyObject[] ecopts_p = {context.nil};
1029+
ecflags = econvPrepareOpts(context, tmp, ecopts_p);
1030+
return strTranscode0(context, str, ecflags, ecopts_p[0], result);
10051031
}
10061032

1007-
if (argc == 0) {
1008-
arg1 = runtime.getEncodingService().getDefaultInternal();
1009-
if (arg1 == null || arg1.isNil()) {
1010-
if (ecflags == 0) return null;
1011-
arg1 = objEncoding(context, str);
1012-
}
1013-
if ((ecflags & EConvFlags.INVALID_MASK) == 0) {
1014-
explicitlyInvalidReplace = false;
1015-
}
1016-
ecflags |= EConvFlags.INVALID_REPLACE | EConvFlags.UNDEF_REPLACE;
1017-
} else {
1018-
arg1 = args[0];
1033+
return strTranscode(context, toEncoding, context.nil, str, ecflags, ecopts, result, true);
1034+
}
1035+
1036+
private static IRubyObject strTranscode2(ThreadContext context, IRubyObject toEncoding, IRubyObject forceEncoding, RubyString str, int ecflags, IRubyObject ecopts, TranscodeResult result) {
1037+
IRubyObject tmp = TypeConverter.checkHashType(context.runtime, forceEncoding);
1038+
if (!tmp.isNil()) {
1039+
IRubyObject[] ecopts_p = {context.nil};
1040+
ecflags = econvPrepareOpts(context, tmp, ecopts_p);
1041+
return strTranscode1(context, toEncoding, str, ecflags, ecopts_p[0], result);
10191042
}
10201043

1021-
arg2 = argc <= 1 ? context.nil : args[1];
1022-
dencindex = strTranscodeEncArgs(context, str, arg1, arg2, sname_p, senc_p, dname_p, denc_p);
1044+
return strTranscode(context, toEncoding, forceEncoding, str, ecflags, ecopts, result, true);
1045+
}
1046+
1047+
private static RubyString strTranscode(ThreadContext context, IRubyObject toEncoding, IRubyObject forceEncoding, RubyString str, int ecflags, IRubyObject ecopts, TranscodeResult result, boolean explicitlyInvalidReplace) {
1048+
Ruby runtime = context.runtime;
1049+
1050+
Encoding[] senc_p = {null}, denc_p = {null};
1051+
byte[][] sname_p = {null}, dname_p = {null};
1052+
Encoding dencindex = strTranscodeEncArgs(context, str, toEncoding, forceEncoding, sname_p, senc_p, dname_p, denc_p);
10231053

1024-
IRubyObject dest;
1054+
RubyString dest;
10251055

10261056
if (noDecorators(ecflags)) {
1057+
dest = str;
10271058
if (senc_p[0] != null && senc_p[0] == denc_p[0]) {
10281059
if ((ecflags & EConvFlags.INVALID_MASK) != 0 && explicitlyInvalidReplace) {
10291060
IRubyObject rep = context.nil;
10301061
if (!ecopts.isNil()) {
1031-
rep = ((RubyHash)ecopts).op_aref(context, runtime.newSymbol("replace"));
1062+
rep = ((RubyHash) ecopts).op_aref(context, runtime.newSymbol("replace"));
10321063
}
1033-
dest = ((RubyString)str).encStrScrub(context, senc_p[0], rep, Block.NULL_BLOCK);
1034-
if (dest.isNil()) dest = str;
1035-
self_p[0] = dest;
1036-
return dencindex;
1037-
}
1038-
return arg2.isNil() ? null : dencindex;
1039-
} else if (senc_p[0] != null && denc_p[0] != null && senc_p[0].isAsciiCompatible() && denc_p[0].isAsciiCompatible()) {
1040-
if (((RubyString)str).scanForCodeRange() == StringSupport.CR_7BIT) {
1041-
return dencindex;
1064+
IRubyObject scrubbed = str.encStrScrub(context, senc_p[0], rep, Block.NULL_BLOCK);
1065+
if (scrubbed.isNil()) {
1066+
dest = str;
1067+
} else {
1068+
dest = (RubyString) scrubbed;
1069+
}
1070+
} else if (forceEncoding.isNil()){
1071+
dencindex = null;
10421072
}
1043-
}
1044-
if (encodingEqual(sname_p[0], dname_p[0])) {
1045-
return arg2.isNil() ? null : dencindex;
1073+
return result.apply(context, str, dencindex, dest);
1074+
} else if (senc_p[0] != null && denc_p[0] != null
1075+
&& senc_p[0].isAsciiCompatible() && denc_p[0].isAsciiCompatible()
1076+
&& str.scanForCodeRange() == StringSupport.CR_7BIT) {
1077+
return result.apply(context, str, dencindex, str);
1078+
} else if (encodingEqual(sname_p[0], dname_p[0])) {
1079+
if (forceEncoding.isNil()) dencindex = null;
1080+
return result.apply(context, str, dencindex, str);
10461081
}
10471082
} else {
10481083
if (encodingEqual(sname_p[0], dname_p[0])) {
@@ -1051,12 +1086,12 @@ public static Encoding strTranscode0(ThreadContext context, int argc, IRubyObjec
10511086
}
10521087
}
10531088

1054-
ByteList sp = ((RubyString)str).getByteList();
1089+
ByteList sp = str.getByteList();
10551090
ByteList fromp = sp;
1056-
int slen = ((RubyString)str).size();
1091+
int slen = str.size();
10571092
int blen = slen + 30;
10581093
dest = RubyString.newStringLight(runtime, blen);
1059-
ByteList destp = ((RubyString)dest).getByteList();
1094+
ByteList destp = dest.getByteList();
10601095

10611096
byte[] frompBytes = fromp.unsafeBytes();
10621097
byte[] destpBytes = destp.unsafeBytes();
@@ -1074,9 +1109,7 @@ public static Encoding strTranscode0(ThreadContext context, int argc, IRubyObjec
10741109
dencindex = defineDummyEncoding(context, dname_p[0]);
10751110
}
10761111

1077-
self_p[0] = dest;
1078-
1079-
return dencindex;
1112+
return result.apply(context, str, dencindex, dest);
10801113
}
10811114

10821115
// rb_obj_encoding
@@ -2341,4 +2374,64 @@ public static Encoding ioStripBOM(RubyIO io) {
23412374
return ioStripBOM(io.getRuntime().getCurrentContext(), io);
23422375
}
23432376

2377+
@Deprecated
2378+
public static Encoding strTranscode0(ThreadContext context, int argc, IRubyObject[] args, IRubyObject[] self_p, int ecflags, IRubyObject ecopts) {
2379+
Encoding[] enc_p = {null};
2380+
TranscodeResult result = (ctx, str, enc, newStr) -> {enc_p[0] = enc; self_p[0] = newStr; return newStr;};
2381+
switch (argc) {
2382+
case 0:
2383+
strTranscode0(context, (RubyString) self_p[0], ecflags, ecopts, result);
2384+
return enc_p[0];
2385+
case 1:
2386+
strTranscode1(context, args[0], (RubyString) self_p[0], ecflags, ecopts, result);
2387+
return enc_p[0];
2388+
case 2:
2389+
strTranscode2(context, args[0], args[1], (RubyString) self_p[0], ecflags, ecopts, result);
2390+
return enc_p[0];
2391+
default:
2392+
throw context.runtime.newArgumentError(args.length, 2);
2393+
}
2394+
}
2395+
2396+
@Deprecated
2397+
public static Encoding strTranscode(ThreadContext context, IRubyObject[] args, IRubyObject[] self_p) {
2398+
Encoding[] enc_p = {null};
2399+
TranscodeResult result = (ctx, str, enc, newStr) -> {enc_p[0] = enc; self_p[0] = newStr; return newStr;};
2400+
2401+
strTranscode(context, args, (RubyString) self_p[0], result);
2402+
2403+
return enc_p[0];
2404+
}
2405+
2406+
@Deprecated
2407+
public static IRubyObject strEncode(ThreadContext context, IRubyObject str, IRubyObject... args) {
2408+
return strTranscode(context, args, (RubyString) str, EncodingUtils::encodedDup);
2409+
}
2410+
2411+
@Deprecated
2412+
public static IRubyObject encodedDup(ThreadContext context, IRubyObject newstr, IRubyObject str, Encoding encindex) {
2413+
return encodedDup(context, (RubyString) newstr, encindex, (RubyString) str);
2414+
}
2415+
2416+
@Deprecated
2417+
public static IRubyObject strEncodeAssociate(ThreadContext context, IRubyObject str, Encoding encidx) {
2418+
return strEncodeAssociate((RubyString) str, encidx);
2419+
}
2420+
2421+
@Deprecated
2422+
public static IRubyObject strTranscode(ThreadContext context, IRubyObject[] args, RubyString str, TranscodeResult result) {
2423+
switch (args.length) {
2424+
case 0:
2425+
return strTranscode(context, str, result);
2426+
case 1:
2427+
return strTranscode(context, args[0], str, result);
2428+
case 2:
2429+
return strTranscode(context, args[0], args[1], str, result);
2430+
case 3:
2431+
return strTranscode(context, args[0], args[1], args[2], str, result);
2432+
default:
2433+
throw context.runtime.newArgumentError(args.length, 2);
2434+
}
2435+
}
2436+
23442437
}

0 commit comments

Comments
 (0)