Skip to content

Commit 93e9779

Browse files
sampokuokkanenXrXr
authored andcommitted
Define String#ascii_only? and #valid_encoding? in Ruby
Get rid of the CFUNC overhead in String#ascii_only? and #valid_encoding?. Define them as leaf Primitive.cexpr! builtins in a new string.rb, following the pattern of numeric.rb. This makes them 1.2x-1.4x faster across the interpreter, YJIT, and ZJIT. Move ZJIT's cfunc annotation for String#ascii_only? to annotate_builtin!, and add one for String#valid_encoding?. Fix CI for builtin String#ascii_only?
1 parent 5465dca commit 93e9779

9 files changed

Lines changed: 51 additions & 27 deletions

File tree

benchmark/string_ascii_only_p.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
prelude: |
2+
ascii = "hello world"
3+
utf8 = "héllo wörld 晦"
4+
benchmark:
5+
ascii_only_true: ascii.ascii_only?
6+
ascii_only_false: utf8.ascii_only?
7+
loop_count: 20000000
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
prelude: |
2+
utf8 = "héllo wörld 晦"
3+
broken = "\xff\xfe".dup.force_encoding("UTF-8")
4+
benchmark:
5+
valid_enc_true: utf8.valid_encoding?
6+
valid_enc_false: broken.valid_encoding?
7+
loop_count: 20000000

common.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,7 @@ BUILTIN_RB_SRCS = \
11991199
$(srcdir)/kernel.rb \
12001200
$(srcdir)/pathname_builtin.rb \
12011201
$(srcdir)/ractor.rb \
1202+
$(srcdir)/string.rb \
12021203
$(srcdir)/symbol.rb \
12031204
$(srcdir)/timev.rb \
12041205
$(srcdir)/thread_sync.rb \

depend

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17718,6 +17718,7 @@ string.$(OBJEXT): {$(VPATH)}backward/2/limits.h
1771817718
string.$(OBJEXT): {$(VPATH)}backward/2/long_long.h
1771917719
string.$(OBJEXT): {$(VPATH)}backward/2/stdalign.h
1772017720
string.$(OBJEXT): {$(VPATH)}backward/2/stdarg.h
17721+
string.$(OBJEXT): {$(VPATH)}builtin.h
1772117722
string.$(OBJEXT): {$(VPATH)}config.h
1772217723
string.$(OBJEXT): {$(VPATH)}constant.h
1772317724
string.$(OBJEXT): {$(VPATH)}debug_counter.h
@@ -17896,6 +17897,7 @@ string.$(OBJEXT): {$(VPATH)}rubyparser.h
1789617897
string.$(OBJEXT): {$(VPATH)}shape.h
1789717898
string.$(OBJEXT): {$(VPATH)}st.h
1789817899
string.$(OBJEXT): {$(VPATH)}string.c
17900+
string.$(OBJEXT): {$(VPATH)}string.rbinc
1789917901
string.$(OBJEXT): {$(VPATH)}subst.h
1790017902
string.$(OBJEXT): {$(VPATH)}thread.h
1790117903
string.$(OBJEXT): {$(VPATH)}thread_$(THREAD_MODEL).h

inits.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ rb_call_builtin_inits(void)
105105
BUILTIN(warning);
106106
BUILTIN(array);
107107
BUILTIN(hash);
108+
BUILTIN(string);
108109
BUILTIN(symbol);
109110
BUILTIN(timev);
110111
BUILTIN(thread_sync);

string.c

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11736,14 +11736,6 @@ rb_str_b(VALUE str)
1173611736
return str2;
1173711737
}
1173811738

11739-
/*
11740-
* call-seq:
11741-
* valid_encoding? -> true or false
11742-
*
11743-
* :include: doc/string/valid_encoding_p.rdoc
11744-
*
11745-
*/
11746-
1174711739
static VALUE
1174811740
rb_str_valid_encoding_p(VALUE str)
1174911741
{
@@ -11752,18 +11744,6 @@ rb_str_valid_encoding_p(VALUE str)
1175211744
return RBOOL(cr != ENC_CODERANGE_BROKEN);
1175311745
}
1175411746

11755-
/*
11756-
* call-seq:
11757-
* ascii_only? -> true or false
11758-
*
11759-
* Returns whether +self+ contains only ASCII characters:
11760-
*
11761-
* 'abc'.ascii_only? # => true
11762-
* "abc\u{6666}".ascii_only? # => false
11763-
*
11764-
* Related: see {Querying}[rdoc-ref:String@Querying].
11765-
*/
11766-
1176711747
static VALUE
1176811748
rb_str_is_ascii_only_p(VALUE str)
1176911749
{
@@ -13053,8 +13033,6 @@ Init_String(void)
1305313033
rb_define_method(rb_cString, "encoding", rb_obj_encoding, 0); /* in encoding.c */
1305413034
rb_define_method(rb_cString, "force_encoding", rb_str_force_encoding, 1);
1305513035
rb_define_method(rb_cString, "b", rb_str_b, 0);
13056-
rb_define_method(rb_cString, "valid_encoding?", rb_str_valid_encoding_p, 0);
13057-
rb_define_method(rb_cString, "ascii_only?", rb_str_is_ascii_only_p, 0);
1305813036

1305913037
/* define UnicodeNormalize module here so that we don't have to look it up */
1306013038
mUnicodeNormalize = rb_define_module("UnicodeNormalize");
@@ -13107,3 +13085,5 @@ Init_String(void)
1310713085
rb_define_method(rb_cSymbol, "encoding", sym_encoding, 0);
1310813086
}
1310913087

13088+
#include "string.rbinc"
13089+

string.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
class String
2+
# call-seq:
3+
# valid_encoding? -> true or false
4+
#
5+
# :include: doc/string/valid_encoding_p.rdoc
6+
#
7+
def valid_encoding?
8+
Primitive.attr! :leaf
9+
Primitive.cexpr! 'rb_str_valid_encoding_p(self)'
10+
end
11+
12+
# call-seq:
13+
# ascii_only? -> true or false
14+
#
15+
# Returns whether +self+ contains only ASCII characters:
16+
#
17+
# 'abc'.ascii_only? # => true
18+
# "abc\u{6666}".ascii_only? # => false
19+
#
20+
# Related: see {Querying}[rdoc-ref:String@Querying].
21+
def ascii_only?
22+
Primitive.attr! :leaf
23+
Primitive.cexpr! 'rb_str_is_ascii_only_p(self)'
24+
end
25+
end

zjit/src/cruby_methods.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,9 +222,6 @@ pub fn init() -> Annotations {
222222
annotate!(rb_cString, "<<", inline_string_append);
223223
annotate!(rb_cString, "==", inline_string_eq);
224224
// Not elidable; has a side effect of setting the encoding if ENC_CODERANGE_UNKNOWN.
225-
// TOOD(max): Turn this into a load/compare. Will need to side-exit or do the full call if
226-
// ENC_CODERANGE_UNKNOWN.
227-
annotate!(rb_cString, "ascii_only?", types::BoolExact, no_gc, leaf);
228225
annotate!(rb_cModule, "name", types::StringExact.union(types::NilClass), no_gc, leaf, elidable);
229226
annotate!(rb_cModule, "===", inline_module_eqq, types::BoolExact, no_gc, leaf);
230227
annotate!(rb_cArray, "length", inline_array_length, types::Fixnum, no_gc, leaf, elidable);
@@ -291,6 +288,10 @@ pub fn init() -> Annotations {
291288
annotate_builtin!(rb_mKernel, "frozen?", types::BoolExact);
292289
annotate_builtin!(rb_cSymbol, "name", types::StringExact);
293290
annotate_builtin!(rb_cSymbol, "to_s", types::StringExact);
291+
// TOOD(max): Turn this into a load/compare. Will need to side-exit or do the full call if
292+
// ENC_CODERANGE_UNKNOWN.
293+
annotate_builtin!(rb_cString, "ascii_only?", types::BoolExact, no_gc, leaf);
294+
annotate_builtin!(rb_cString, "valid_encoding?", types::BoolExact, no_gc, leaf);
294295

295296
// Array iteration builtins (used in with_jit Array#each, map, select, find)
296297
builtin_funcs.insert(rb_jit_fixnum_inc as *mut c_void, FnProperties { inline: inline_fixnum_inc, return_type: types::Fixnum, ..Default::default() });

zjit/src/hir/opt_tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11910,8 +11910,8 @@ mod hir_opt_tests {
1191011910
bb3(v9:BasicObject, v10:BasicObject):
1191111911
PatchPoint NoSingletonClass(String@0x1008)
1191211912
PatchPoint MethodRedefined(String@0x1008, ascii_only?@0x1010, cme:0x1018)
11913-
v24:StringExact = GuardType v10, StringExact recompile
11914-
v25:BoolExact = CCall v24, :String#ascii_only?@0x1040
11913+
v23:StringExact = GuardType v10, StringExact recompile
11914+
v25:BoolExact = InvokeBuiltin leaf <inline_expr>, v23
1191511915
CheckInterrupts
1191611916
Return v25
1191711917
");

0 commit comments

Comments
 (0)