Open elfham opened 1 year ago
String.new の :capacity の説明ですが、
String.new
:capacity
内部バッファのサイズを指定します。指定することで、なんども文字列連結する (そしてreallocがなんども呼ばれる)ときのパフォーマンスが改善されるかもしれません。省略した場合、引数stringのバイト数が127未満であれば127、それ以上であればstring.bytesizeになります。
以前のバージョンでの仕様は分からないのですが、 3.1.0 以降では、省略時のデフォルトの説明が実際の動作と合っていないように思われます。
:capacity は RString の as.heap.aux.capa に相当するかと思いますが、実際には以下のような動作になるようです。
RString
as.heap.aux.capa
※ 3.1.0 (USE_RVARGC 未指定) では 24 バイトから、 3.2.0 では 16 バイトから
説明のうまい修正案がちょっと思い付きませんが、「省略した場合」以降は削ってしまっても良いように思います。
メソッドシグネチャーの
new(string = "") -> String new(string = "", encoding: string.encoding, capacity: 63) -> String new(string = "", encoding: string.encoding, capacity: string.bytesize) -> String
も悩ましいですが、少なくとも string.bytesize になることは無いようなので 3 つ目は無くて良いように思います。
string.bytesize
確認に使用したスクリプトと実行結果を以下に示します。
require 'inline' class String def super_inspect self.class.superclass.instance_method(:inspect).bind(self).call end raise NotImplementedError, 'Ruby 3.1.0 required' if RUBY_VERSION < '3.1.0' inline do |builder| builder.include '<stdio.h>' builder.add_compile_flags '-Wall' builder.c_raw <<~CODE VALUE inspect_rstring(int argc, VALUE *argv, VALUE self) { FILE *o = stdout; if (argc == 1) { /* Output Title */ if (TYPE(argv[0]) != T_STRING) rb_raise(rb_eTypeError, "Title Not String"); fprintf(o, "%s\\n", RSTRING_PTR(argv[0])); } char *rstring_ptr = RSTRING_PTR(self); long rstring_len = RSTRING_LEN(self); bool str_embed = ! (RBASIC(self)->flags & RSTRING_NOEMBED); bool str_shared = RBASIC(self)->flags & ELTS_SHARED; struct RString *rstring = RSTRING(self); fprintf(o, "VALUE: 0x%lx\\n", self); fprintf(o, "OBJ_FROZEN: %d\\n", OBJ_FROZEN(self)); fprintf(o, "STR_EMBED_P: %d\\n", str_embed); fprintf(o, "STR_SHARED_P: %d\\n", str_shared); fprintf(o, "RSTRING_PTR: %p\\n", rstring_ptr); fprintf(o, "RSTRING_LEN: %ld\\n", rstring_len); if (str_embed) { #if USE_RVARGC fprintf(o, "rstring->as.embed.len: %ld\\n", rstring->as.embed.len); #endif fprintf(o, "rstring->as.embed.ary: %p\\n", rstring->as.embed.ary); fprintf(o, "rstring->as.embed.ary: \\"%s\\"\\n", rstring->as.embed.ary); } else { fprintf(o, "rstring->as.heap.len: %ld\\n", rstring->as.heap.len); fprintf(o, "rstring->as.heap.ptr: %p\\n", rstring->as.heap.ptr); if (str_shared) { fprintf(o, "rstring->as.heap.aux.shared: 0x%lx\\n", rstring->as.heap.aux.shared); } else { fprintf(o, "rstring->as.heap.aux.capa: %ld\\n", rstring->as.heap.aux.capa); } } fprintf(o, "\\n"); fflush(o); return Qnil; } CODE end end module Kernel inline do |builder| builder.add_compile_flags '-Wall' # XXX: DANGER!!! builder.c_raw <<~CODE VALUE draw_object_by_value(int argc, VALUE *argv, VALUE _kernel) { if (argc != 1) return Qnil; unsigned long value = (VALUE)NUM2ULONG(argv[0]); return (VALUE)value; } CODE end end
% ruby -v ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e) [x86_64-freebsd13.1] % irb -I . -r inspect_rstring irb(main):001:0> String.new("*" * 23).inspect_rstring VALUE: 0x804f94cd0 OBJ_FROZEN: 0 STR_EMBED_P: 1 STR_SHARED_P: 1 RSTRING_PTR: 0x804f94ce0 RSTRING_LEN: 23 rstring->as.embed.ary: 0x804f94ce0 rstring->as.embed.ary: "***********************" => nil irb(main):002:0> String.new("*" * 24).inspect_rstring VALUE: 0x8062f0ab8 OBJ_FROZEN: 0 STR_EMBED_P: 0 STR_SHARED_P: 1 RSTRING_PTR: 0x8051b5760 RSTRING_LEN: 24 rstring->as.heap.len: 24 rstring->as.heap.ptr: 0x8051b5760 rstring->as.heap.aux.shared: 0x8062f0a90 => nil irb(main):003:0> String.new(capacity: 62).inspect_rstring VALUE: 0x805ae96d0 OBJ_FROZEN: 0 STR_EMBED_P: 0 STR_SHARED_P: 0 RSTRING_PTR: 0x805292cc0 RSTRING_LEN: 0 rstring->as.heap.len: 0 rstring->as.heap.ptr: 0x805292cc0 rstring->as.heap.aux.capa: 63 => nil irb(main):004:0>
% ruby -v ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-freebsd13.1] % irb -I . -r inspect_rstring irb(main):001:0> String.new("*" * 15).inspect_rstring VALUE: 0x80c643700 OBJ_FROZEN: 0 STR_EMBED_P: 1 STR_SHARED_P: 0 RSTRING_PTR: 0x80c643718 RSTRING_LEN: 15 rstring->as.embed.len: 15 rstring->as.embed.ary: 0x80c643718 rstring->as.embed.ary: "***************" => nil irb(main):002:0> String.new("*" * 16).inspect_rstring VALUE: 0x80c604f00 OBJ_FROZEN: 0 STR_EMBED_P: 0 STR_SHARED_P: 1 RSTRING_PTR: 0x8084eb388 RSTRING_LEN: 16 rstring->as.heap.len: 16 rstring->as.heap.ptr: 0x8084eb388 rstring->as.heap.aux.shared: 0x8084eb370 => nil irb(main):003:0> String.new(capacity: 62).inspect_rstring VALUE: 0x80c5c71c8 OBJ_FROZEN: 0 STR_EMBED_P: 0 STR_SHARED_P: 0 RSTRING_PTR: 0x807251b00 RSTRING_LEN: 0 rstring->as.heap.len: 0 rstring->as.heap.ptr: 0x807251b00 rstring->as.heap.aux.capa: 63 => nil irb(main):004:0>
# あれ?余談ですが、 3.1.3 では STR_EMBED_P と STR_SHARED_P の両方が立つことがあるんですね……。
STR_EMBED_P
STR_SHARED_P
String.new
の:capacity
の説明ですが、以前のバージョンでの仕様は分からないのですが、 3.1.0 以降では、省略時のデフォルトの説明が実際の動作と合っていないように思われます。
:capacity
はRString
のas.heap.aux.capa
に相当するかと思いますが、実際には以下のような動作になるようです。:capacity
の指定の無い場合RString
に embed されるので capa を持たない:capacity
を指定した場合as.heap.aux.capa
は 63 になるas.heap.aux.capa
は指定した値になる※ 3.1.0 (USE_RVARGC 未指定) では 24 バイトから、 3.2.0 では 16 バイトから
説明のうまい修正案がちょっと思い付きませんが、「省略した場合」以降は削ってしまっても良いように思います。
メソッドシグネチャーの
も悩ましいですが、少なくとも
string.bytesize
になることは無いようなので 3 つ目は無くて良いように思います。確認に使用したスクリプトと実行結果を以下に示します。