fixed encoding table
This reduces global lock acquiring for reading. https://bugs.ruby-lang.org/issues/18949
This commit is contained in:
parent
15b60bb1a4
commit
ae19ac5b5b
Notes:
git
2022-12-16 01:04:55 +00:00
2
NEWS.md
2
NEWS.md
@ -202,6 +202,8 @@ Note: We're only listing outstanding class updates.
|
|||||||
try to dynamically guess the endian based on a byte order mark.
|
try to dynamically guess the endian based on a byte order mark.
|
||||||
Use `Encoding::UTF_16BE`/`UTF_16LE` and `Encoding::UTF_32BE`/`UTF_32LE` instead.
|
Use `Encoding::UTF_16BE`/`UTF_16LE` and `Encoding::UTF_32BE`/`UTF_32LE` instead.
|
||||||
This change speeds up getting the encoding of a String. [[Feature #18949]]
|
This change speeds up getting the encoding of a String. [[Feature #18949]]
|
||||||
|
* Limit maximum encoding set size by 256.
|
||||||
|
If exceeding maximum size, `EncodingError` will be raised. [[Feature #18949]]
|
||||||
|
|
||||||
* Enumerator
|
* Enumerator
|
||||||
|
|
||||||
|
@ -1474,7 +1474,7 @@ assert_equal "#{N}#{N}", %Q{
|
|||||||
}
|
}
|
||||||
|
|
||||||
# enc_table
|
# enc_table
|
||||||
assert_equal "#{N/10}", %Q{
|
assert_equal "100", %Q{
|
||||||
Ractor.new do
|
Ractor.new do
|
||||||
loop do
|
loop do
|
||||||
Encoding.find("test-enc-#{rand(5_000)}").inspect
|
Encoding.find("test-enc-#{rand(5_000)}").inspect
|
||||||
@ -1483,7 +1483,7 @@ assert_equal "#{N/10}", %Q{
|
|||||||
end
|
end
|
||||||
|
|
||||||
src = Encoding.find("UTF-8")
|
src = Encoding.find("UTF-8")
|
||||||
#{N/10}.times{|i|
|
100.times{|i|
|
||||||
src.replicate("test-enc-\#{i}")
|
src.replicate("test-enc-\#{i}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
144
encoding.c
144
encoding.c
@ -56,9 +56,8 @@ int rb_encdb_alias(const char *alias, const char *orig);
|
|||||||
static ID id_encoding;
|
static ID id_encoding;
|
||||||
VALUE rb_cEncoding;
|
VALUE rb_cEncoding;
|
||||||
|
|
||||||
#define DEFAULT_ENCODING_LIST_CAPA 128
|
#define ENCODING_LIST_CAPA 256
|
||||||
static VALUE rb_default_encoding_list;
|
static VALUE rb_encoding_list;
|
||||||
static VALUE rb_additional_encoding_list;
|
|
||||||
|
|
||||||
struct rb_encoding_entry {
|
struct rb_encoding_entry {
|
||||||
const char *name;
|
const char *name;
|
||||||
@ -67,9 +66,8 @@ struct rb_encoding_entry {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct enc_table {
|
static struct enc_table {
|
||||||
struct rb_encoding_entry *list;
|
struct rb_encoding_entry list[ENCODING_LIST_CAPA];
|
||||||
int count;
|
int count;
|
||||||
int size;
|
|
||||||
st_table *names;
|
st_table *names;
|
||||||
} global_enc_table;
|
} global_enc_table;
|
||||||
|
|
||||||
@ -128,47 +126,25 @@ enc_new(rb_encoding *encoding)
|
|||||||
static void
|
static void
|
||||||
enc_list_update(int index, rb_raw_encoding *encoding)
|
enc_list_update(int index, rb_raw_encoding *encoding)
|
||||||
{
|
{
|
||||||
if (index < DEFAULT_ENCODING_LIST_CAPA) {
|
RUBY_ASSERT(index < ENCODING_LIST_CAPA);
|
||||||
VALUE list = rb_default_encoding_list;
|
|
||||||
|
VALUE list = rb_encoding_list;
|
||||||
if (list && NIL_P(rb_ary_entry(list, index))) {
|
if (list && NIL_P(rb_ary_entry(list, index))) {
|
||||||
/* initialize encoding data */
|
/* initialize encoding data */
|
||||||
rb_ary_store(list, index, enc_new(encoding));
|
rb_ary_store(list, index, enc_new(encoding));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
RB_VM_LOCK_ENTER();
|
|
||||||
{
|
|
||||||
VALUE list = rb_additional_encoding_list;
|
|
||||||
if (list && NIL_P(rb_ary_entry(list, index))) {
|
|
||||||
/* initialize encoding data */
|
|
||||||
rb_ary_store(list, index - DEFAULT_ENCODING_LIST_CAPA, enc_new(encoding));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RB_VM_LOCK_LEAVE();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
enc_list_lookup(int idx)
|
enc_list_lookup(int idx)
|
||||||
{
|
{
|
||||||
VALUE list, enc;
|
VALUE list, enc = Qnil;
|
||||||
|
|
||||||
if (idx < DEFAULT_ENCODING_LIST_CAPA) {
|
if (idx < ENCODING_LIST_CAPA) {
|
||||||
if (!(list = rb_default_encoding_list)) {
|
list = rb_encoding_list;
|
||||||
rb_bug("rb_enc_from_encoding_index(%d): no rb_default_encoding_list", idx);
|
RUBY_ASSERT(list);
|
||||||
}
|
|
||||||
enc = rb_ary_entry(list, idx);
|
enc = rb_ary_entry(list, idx);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
RB_VM_LOCK_ENTER();
|
|
||||||
{
|
|
||||||
if (!(list = rb_additional_encoding_list)) {
|
|
||||||
rb_bug("rb_enc_from_encoding_index(%d): no rb_additional_encoding_list", idx);
|
|
||||||
}
|
|
||||||
enc = rb_ary_entry(list, idx - DEFAULT_ENCODING_LIST_CAPA);
|
|
||||||
}
|
|
||||||
RB_VM_LOCK_LEAVE();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NIL_P(enc)) {
|
if (NIL_P(enc)) {
|
||||||
rb_bug("rb_enc_from_encoding_index(%d): not created yet", idx);
|
rb_bug("rb_enc_from_encoding_index(%d): not created yet", idx);
|
||||||
@ -345,16 +321,10 @@ rb_find_encoding(VALUE enc)
|
|||||||
static int
|
static int
|
||||||
enc_table_expand(struct enc_table *enc_table, int newsize)
|
enc_table_expand(struct enc_table *enc_table, int newsize)
|
||||||
{
|
{
|
||||||
struct rb_encoding_entry *ent;
|
if (newsize > ENCODING_LIST_CAPA) {
|
||||||
int count = newsize;
|
rb_raise(rb_eEncodingError, "too many encoding (> %d)", ENCODING_LIST_CAPA);
|
||||||
|
}
|
||||||
if (enc_table->size >= newsize) return newsize;
|
return newsize;
|
||||||
newsize = (newsize + 7) / 8 * 8;
|
|
||||||
ent = REALLOC_N(enc_table->list, struct rb_encoding_entry, newsize);
|
|
||||||
memset(ent + enc_table->size, 0, sizeof(*ent)*(newsize - enc_table->size));
|
|
||||||
enc_table->list = ent;
|
|
||||||
enc_table->size = newsize;
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -413,17 +383,7 @@ enc_from_index(struct enc_table *enc_table, int index)
|
|||||||
rb_encoding *
|
rb_encoding *
|
||||||
rb_enc_from_index(int index)
|
rb_enc_from_index(int index)
|
||||||
{
|
{
|
||||||
rb_encoding *enc;
|
return enc_from_index(&global_enc_table, index);
|
||||||
|
|
||||||
switch (index) {
|
|
||||||
case ENCINDEX_ASCII_8BIT: return global_enc_ascii;
|
|
||||||
case ENCINDEX_UTF_8: return global_enc_utf_8;
|
|
||||||
case ENCINDEX_US_ASCII: return global_enc_us_ascii;
|
|
||||||
default:
|
|
||||||
GLOBAL_ENC_TABLE_EVAL(enc_table,
|
|
||||||
enc = enc_from_index(enc_table, index));
|
|
||||||
return enc;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -484,11 +444,14 @@ rb_encdb_declare(const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
enc_check_duplication(struct enc_table *enc_table, const char *name)
|
enc_check_addable(struct enc_table *enc_table, const char *name)
|
||||||
{
|
{
|
||||||
if (enc_registered(enc_table, name) >= 0) {
|
if (enc_registered(enc_table, name) >= 0) {
|
||||||
rb_raise(rb_eArgError, "encoding %s is already registered", name);
|
rb_raise(rb_eArgError, "encoding %s is already registered", name);
|
||||||
}
|
}
|
||||||
|
else if (!valid_encoding_name_p(name)) {
|
||||||
|
rb_raise(rb_eArgError, "invalid encoding name: %s", name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static rb_encoding*
|
static rb_encoding*
|
||||||
@ -524,11 +487,7 @@ rb_enc_set_base(const char *name, const char *orig)
|
|||||||
int
|
int
|
||||||
rb_enc_set_dummy(int index)
|
rb_enc_set_dummy(int index)
|
||||||
{
|
{
|
||||||
rb_encoding *enc;
|
rb_encoding *enc = global_enc_table.list[index].enc;
|
||||||
|
|
||||||
GLOBAL_ENC_TABLE_EVAL(enc_table,
|
|
||||||
enc = enc_table->list[index].enc);
|
|
||||||
|
|
||||||
ENC_SET_DUMMY((rb_raw_encoding *)enc);
|
ENC_SET_DUMMY((rb_raw_encoding *)enc);
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
@ -538,7 +497,7 @@ enc_replicate(struct enc_table *enc_table, const char *name, rb_encoding *encodi
|
|||||||
{
|
{
|
||||||
int idx;
|
int idx;
|
||||||
|
|
||||||
enc_check_duplication(enc_table, name);
|
enc_check_addable(enc_table, name);
|
||||||
idx = enc_register(enc_table, name, encoding);
|
idx = enc_register(enc_table, name, encoding);
|
||||||
if (idx < 0) rb_raise(rb_eArgError, "invalid encoding name: %s", name);
|
if (idx < 0) rb_raise(rb_eArgError, "invalid encoding name: %s", name);
|
||||||
set_base_encoding(enc_table, idx, encoding);
|
set_base_encoding(enc_table, idx, encoding);
|
||||||
@ -727,7 +686,7 @@ rb_enc_alias(const char *alias, const char *orig)
|
|||||||
|
|
||||||
GLOBAL_ENC_TABLE_ENTER(enc_table);
|
GLOBAL_ENC_TABLE_ENTER(enc_table);
|
||||||
{
|
{
|
||||||
enc_check_duplication(enc_table, alias);
|
enc_check_addable(enc_table, alias);
|
||||||
if ((idx = rb_enc_find_index(orig)) < 0) {
|
if ((idx = rb_enc_find_index(orig)) < 0) {
|
||||||
r = -1;
|
r = -1;
|
||||||
}
|
}
|
||||||
@ -764,7 +723,7 @@ rb_enc_init(struct enc_table *enc_table)
|
|||||||
{
|
{
|
||||||
enc_table_expand(enc_table, ENCODING_COUNT + 1);
|
enc_table_expand(enc_table, ENCODING_COUNT + 1);
|
||||||
if (!enc_table->names) {
|
if (!enc_table->names) {
|
||||||
enc_table->names = st_init_strcasetable();
|
enc_table->names = st_init_strcasetable_with_size(ENCODING_LIST_CAPA);
|
||||||
}
|
}
|
||||||
#define OnigEncodingASCII_8BIT OnigEncodingASCII
|
#define OnigEncodingASCII_8BIT OnigEncodingASCII
|
||||||
#define ENC_REGISTER(enc) enc_register_at(enc_table, ENCINDEX_##enc, rb_enc_name(&OnigEncoding##enc), &OnigEncoding##enc)
|
#define ENC_REGISTER(enc) enc_register_at(enc_table, ENCINDEX_##enc, rb_enc_name(&OnigEncoding##enc), &OnigEncoding##enc)
|
||||||
@ -877,11 +836,9 @@ rb_enc_autoload(rb_encoding *enc)
|
|||||||
int
|
int
|
||||||
rb_enc_find_index(const char *name)
|
rb_enc_find_index(const char *name)
|
||||||
{
|
{
|
||||||
int i;
|
int i = enc_registered(&global_enc_table, name);
|
||||||
rb_encoding *enc;
|
rb_encoding *enc;
|
||||||
|
|
||||||
GLOBAL_ENC_TABLE_EVAL(enc_table, i = enc_registered(enc_table, name));
|
|
||||||
|
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
i = load_encoding(name);
|
i = load_encoding(name);
|
||||||
}
|
}
|
||||||
@ -1368,10 +1325,7 @@ enc_names(VALUE self)
|
|||||||
|
|
||||||
args[0] = (VALUE)rb_to_encoding_index(self);
|
args[0] = (VALUE)rb_to_encoding_index(self);
|
||||||
args[1] = rb_ary_new2(0);
|
args[1] = rb_ary_new2(0);
|
||||||
|
st_foreach(global_enc_table.names, enc_names_i, (st_data_t)args);
|
||||||
GLOBAL_ENC_TABLE_EVAL(enc_table,
|
|
||||||
st_foreach(enc_table->names, enc_names_i, (st_data_t)args));
|
|
||||||
|
|
||||||
return args[1];
|
return args[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1397,14 +1351,7 @@ static VALUE
|
|||||||
enc_list(VALUE klass)
|
enc_list(VALUE klass)
|
||||||
{
|
{
|
||||||
VALUE ary = rb_ary_new2(0);
|
VALUE ary = rb_ary_new2(0);
|
||||||
|
rb_ary_replace(ary, rb_encoding_list);
|
||||||
RB_VM_LOCK_ENTER();
|
|
||||||
{
|
|
||||||
rb_ary_replace(ary, rb_default_encoding_list);
|
|
||||||
rb_ary_concat(ary, rb_additional_encoding_list);
|
|
||||||
}
|
|
||||||
RB_VM_LOCK_LEAVE();
|
|
||||||
|
|
||||||
return ary;
|
return ary;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1553,15 +1500,17 @@ rb_locale_encindex(void)
|
|||||||
|
|
||||||
if (idx < 0) idx = ENCINDEX_UTF_8;
|
if (idx < 0) idx = ENCINDEX_UTF_8;
|
||||||
|
|
||||||
GLOBAL_ENC_TABLE_ENTER(enc_table);
|
if (enc_registered(&global_enc_table, "locale") < 0) {
|
||||||
if (enc_registered(enc_table, "locale") < 0) {
|
|
||||||
# if defined _WIN32
|
# if defined _WIN32
|
||||||
void Init_w32_codepage(void);
|
void Init_w32_codepage(void);
|
||||||
Init_w32_codepage();
|
Init_w32_codepage();
|
||||||
# endif
|
# endif
|
||||||
|
GLOBAL_ENC_TABLE_ENTER(enc_table);
|
||||||
|
{
|
||||||
enc_alias_internal(enc_table, "locale", idx);
|
enc_alias_internal(enc_table, "locale", idx);
|
||||||
}
|
}
|
||||||
GLOBAL_ENC_TABLE_LEAVE();
|
GLOBAL_ENC_TABLE_LEAVE();
|
||||||
|
}
|
||||||
|
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
@ -1575,13 +1524,8 @@ rb_locale_encoding(void)
|
|||||||
int
|
int
|
||||||
rb_filesystem_encindex(void)
|
rb_filesystem_encindex(void)
|
||||||
{
|
{
|
||||||
int idx;
|
int idx = enc_registered(&global_enc_table, "filesystem");
|
||||||
|
if (idx < 0) idx = ENCINDEX_ASCII_8BIT;
|
||||||
GLOBAL_ENC_TABLE_EVAL(enc_table,
|
|
||||||
idx = enc_registered(enc_table, "filesystem"));
|
|
||||||
|
|
||||||
if (idx < 0)
|
|
||||||
idx = ENCINDEX_ASCII_8BIT;
|
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1872,15 +1816,8 @@ rb_enc_name_list_i(st_data_t name, st_data_t idx, st_data_t arg)
|
|||||||
static VALUE
|
static VALUE
|
||||||
rb_enc_name_list(VALUE klass)
|
rb_enc_name_list(VALUE klass)
|
||||||
{
|
{
|
||||||
VALUE ary;
|
VALUE ary = rb_ary_new2(global_enc_table.names->num_entries);
|
||||||
|
st_foreach(global_enc_table.names, rb_enc_name_list_i, (st_data_t)ary);
|
||||||
GLOBAL_ENC_TABLE_ENTER(enc_table);
|
|
||||||
{
|
|
||||||
ary = rb_ary_new2(enc_table->names->num_entries);
|
|
||||||
st_foreach(enc_table->names, rb_enc_name_list_i, (st_data_t)ary);
|
|
||||||
}
|
|
||||||
GLOBAL_ENC_TABLE_LEAVE();
|
|
||||||
|
|
||||||
return ary;
|
return ary;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1926,8 +1863,7 @@ rb_enc_aliases(VALUE klass)
|
|||||||
aliases[0] = rb_hash_new();
|
aliases[0] = rb_hash_new();
|
||||||
aliases[1] = rb_ary_new();
|
aliases[1] = rb_ary_new();
|
||||||
|
|
||||||
GLOBAL_ENC_TABLE_EVAL(enc_table,
|
st_foreach(global_enc_table.names, rb_enc_aliases_enc_i, (st_data_t)aliases);
|
||||||
st_foreach(enc_table->names, rb_enc_aliases_enc_i, (st_data_t)aliases));
|
|
||||||
|
|
||||||
return aliases[0];
|
return aliases[0];
|
||||||
}
|
}
|
||||||
@ -1996,13 +1932,7 @@ Init_Encoding(void)
|
|||||||
|
|
||||||
struct enc_table *enc_table = &global_enc_table;
|
struct enc_table *enc_table = &global_enc_table;
|
||||||
|
|
||||||
if (DEFAULT_ENCODING_LIST_CAPA < enc_table->count) rb_bug("DEFAULT_ENCODING_LIST_CAPA is too small");
|
list = rb_encoding_list = rb_ary_new2(ENCODING_LIST_CAPA);
|
||||||
|
|
||||||
list = rb_additional_encoding_list = rb_ary_new();
|
|
||||||
RBASIC_CLEAR_CLASS(list);
|
|
||||||
rb_gc_register_mark_object(list);
|
|
||||||
|
|
||||||
list = rb_default_encoding_list = rb_ary_new2(DEFAULT_ENCODING_LIST_CAPA);
|
|
||||||
RBASIC_CLEAR_CLASS(list);
|
RBASIC_CLEAR_CLASS(list);
|
||||||
rb_gc_register_mark_object(list);
|
rb_gc_register_mark_object(list);
|
||||||
|
|
||||||
@ -2024,5 +1954,5 @@ Init_encodings(void)
|
|||||||
void
|
void
|
||||||
rb_enc_foreach_name(int (*func)(st_data_t name, st_data_t idx, st_data_t arg), st_data_t arg)
|
rb_enc_foreach_name(int (*func)(st_data_t name, st_data_t idx, st_data_t arg), st_data_t arg)
|
||||||
{
|
{
|
||||||
GLOBAL_ENC_TABLE_EVAL(enc_table, st_foreach(enc_table->names, func, arg));
|
st_foreach(global_enc_table.names, func, arg);
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ class TestEncoding < Test::Unit::TestCase
|
|||||||
def test_extra_encoding
|
def test_extra_encoding
|
||||||
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
||||||
begin;
|
begin;
|
||||||
200.times {|i|
|
100.times {|i|
|
||||||
EnvUtil.suppress_warning { Encoding::UTF_8.replicate("dummy#{i}") }
|
EnvUtil.suppress_warning { Encoding::UTF_8.replicate("dummy#{i}") }
|
||||||
}
|
}
|
||||||
e = Encoding.list.last
|
e = Encoding.list.last
|
||||||
@ -160,4 +160,18 @@ class TestEncoding < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_exceed_encoding_table_size
|
||||||
|
assert_separately(%w[--disable=gems], "#{<<~"begin;"}\n#{<<~'end;'}")
|
||||||
|
begin;
|
||||||
|
begin
|
||||||
|
enc = Encoding::UTF_8
|
||||||
|
1_000.times{|i| EnvUtil.suppress_warning{ enc.replicate("R_#{i}") } } # now 256 entries
|
||||||
|
rescue EncodingError => e
|
||||||
|
assert_match(/too many encoding/, e.message)
|
||||||
|
else
|
||||||
|
assert false
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user