2828#include "id.h"
2929
3030#define ENABLE_ECONV_NEWLINE_OPTION 1
31+ #define SRC_ENC_TO_DST_ENC_KEY_SIZE 128
3132
3233/* VALUE rb_cEncoding = rb_define_class("Encoding", rb_cObject); */
3334static VALUE rb_eUndefinedConversionError ;
@@ -64,7 +65,8 @@ static VALUE sym_finished;
6465static VALUE sym_after_output ;
6566static VALUE sym_incomplete_input ;
6667
67- static VALUE fast_transcoder_table ;
68+ static VALUE fast_transcoder_path_table ;
69+ static VALUE fast_transcoder_entry_table ;
6870
6971static unsigned char *
7072allocate_converted_string (const char * sname , const char * dname ,
@@ -208,11 +210,26 @@ rb_free_transcoder_table(void)
208210 st_free_table (transcoder_table );
209211}
210212
213+ static void
214+ gen_src_to_dst_encodings_key (const char key_buf [SRC_ENC_TO_DST_ENC_KEY_SIZE ], const char * sname , const char * dname )
215+ {
216+ char * p = (char * )key_buf ;
217+ size_t slen = strlen (sname );
218+ memcpy (p , sname , slen );
219+ p += slen ;
220+ memcpy (p , ":" , 1 );
221+ p += 1 ;
222+ size_t dlen = strlen (dname );
223+ RUBY_ASSERT (slen + dlen < SRC_ENC_TO_DST_ENC_KEY_SIZE );
224+ memcpy (p , dname , dlen );
225+ }
226+
211227static transcoder_entry_t *
212228make_transcoder_entry (const char * sname , const char * dname )
213229{
214230 st_data_t val ;
215231 st_table * table2 ;
232+ transcoder_entry_t * entry = NULL ;
216233
217234 RB_VM_LOCKING () {
218235 if (!st_lookup (transcoder_table , (st_data_t )sname , & val )) {
@@ -221,23 +238,39 @@ make_transcoder_entry(const char *sname, const char *dname)
221238 }
222239 table2 = (st_table * )val ;
223240 if (!st_lookup (table2 , (st_data_t )dname , & val )) {
224- transcoder_entry_t * entry = ALLOC (transcoder_entry_t );
241+ entry = ALLOC (transcoder_entry_t );
225242 entry -> sname = sname ;
226243 entry -> dname = dname ;
227244 entry -> lib = NULL ;
228245 entry -> transcoder = NULL ;
229246 val = (st_data_t )entry ;
230247 st_add_direct (table2 , (st_data_t )dname , val );
248+ } else {
249+ entry = (transcoder_entry_t * )val ;
231250 }
232251 }
233- return (transcoder_entry_t * )val ;
252+ char key_buf [SRC_ENC_TO_DST_ENC_KEY_SIZE ] = { 0 };
253+ gen_src_to_dst_encodings_key (key_buf , sname , dname );
254+ VALUE tbl = fast_transcoder_entry_table ;
255+ VALUE new_tbl = rb_managed_id_table_dup (tbl );
256+ rb_managed_id_table_insert (new_tbl , rb_intern (key_buf ), (VALUE )entry );
257+ RUBY_ATOMIC_VALUE_SET (fast_transcoder_entry_table , new_tbl ); // TODO: use CAS
258+ return entry ;
234259}
235260
236261static transcoder_entry_t *
237262get_transcoder_entry (const char * sname , const char * dname )
238263{
239264 st_data_t val = 0 ;
240265 st_table * table2 ;
266+ char key_buf [SRC_ENC_TO_DST_ENC_KEY_SIZE ] = { 0 };
267+ gen_src_to_dst_encodings_key (key_buf , sname , dname );
268+ VALUE entry_val ;
269+ // TODO: once we CAS in `make_transcoder_entry`, we no longer need to check regular transcoder_table after
270+ if (rb_managed_id_table_lookup (fast_transcoder_entry_table , rb_intern (key_buf ), & entry_val )) {
271+ return (transcoder_entry_t * )entry_val ;
272+ }
273+
241274 RB_VM_LOCKING () {
242275 if (st_lookup (transcoder_table , (st_data_t )sname , & val )) {
243276 table2 = (st_table * )val ;
@@ -1032,8 +1065,12 @@ rb_econv_open0(rb_encoding *senc, const char *sname, rb_encoding *denc, const ch
10321065 int num_trans ;
10331066 rb_econv_t * ec ;
10341067
1035- if (* sname && (!senc || !senc -> max_enc_len )) rb_enc_find_index (sname ); // loads encoding if not already loaded
1036- if (* dname && (!denc || !denc -> max_enc_len )) rb_enc_find_index (dname ); // loads encoding if not already loaded
1068+ if (* sname && (!senc || !senc -> max_enc_len )) {
1069+ rb_enc_find_index (sname ); // loads encoding
1070+ }
1071+ if (* dname && (!denc || !denc -> max_enc_len )) {
1072+ rb_enc_find_index (dname ); // loads encoding
1073+ }
10371074
10381075 if (* sname == '\0' && * dname == '\0' ) {
10391076 num_trans = 0 ;
@@ -1044,17 +1081,11 @@ rb_econv_open0(rb_encoding *senc, const char *sname, rb_encoding *denc, const ch
10441081 struct trans_open_t toarg ;
10451082 toarg .entries = NULL ;
10461083 toarg .num_additional = 0 ;
1047- char buf [128 ] = { 0 }; // encoding namelen max is currently 63 bytes, so this is enough
1048- char * p = buf ;
1049- size_t slen = strlen (sname );
1050- memcpy (p , sname , slen );
1051- p += slen ;
1052- memcpy (p , ":" , 1 );
1053- p += 1 ;
1054- memcpy (p , dname , strlen (dname ));
1055- ID src_to_dest_id = rb_intern (buf );
1084+ char key_buf [SRC_ENC_TO_DST_ENC_KEY_SIZE ] = { 0 };
1085+ gen_src_to_dst_encodings_key (key_buf , sname , dname );
1086+ ID src_to_dest_id = rb_intern (key_buf );
10561087 VALUE managed_val ;
1057- VALUE tbl = RUBY_ATOMIC_VALUE_LOAD (fast_transcoder_table );
1088+ VALUE tbl = RUBY_ATOMIC_VALUE_LOAD (fast_transcoder_path_table );
10581089 if (rb_managed_id_table_lookup (tbl , src_to_dest_id , & managed_val )) {
10591090 entries = (transcoder_entry_t * * )managed_val ;
10601091 } else {
@@ -1067,9 +1098,9 @@ rb_econv_open0(rb_encoding *senc, const char *sname, rb_encoding *denc, const ch
10671098 // No need for CAS loop if it's not most recent `fast_transcoder_table`, some values
10681099 // can be lost. It will just go through the slow path next time for the lost src/dst encoding
10691100 // pairs
1070- VALUE new_transcoder_tbl = rb_managed_id_table_dup (tbl );
1071- rb_managed_id_table_insert (new_transcoder_tbl , src_to_dest_id , (VALUE )entries );
1072- RUBY_ATOMIC_VALUE_SET (fast_transcoder_table , new_transcoder_tbl );
1101+ VALUE new_tbl = rb_managed_id_table_dup (tbl );
1102+ rb_managed_id_table_insert (new_tbl , src_to_dest_id , (VALUE )entries );
1103+ RUBY_ATOMIC_VALUE_SET (fast_transcoder_path_table , new_tbl );
10731104 }
10741105 }
10751106
@@ -1159,30 +1190,7 @@ rb_econv_open_enc(rb_encoding *senc, const char *sname, rb_encoding *denc, const
11591190rb_econv_t *
11601191rb_econv_open (const char * sname , const char * dname , int ecflags )
11611192{
1162- rb_econv_t * ec ;
1163- int num_decorators ;
1164- const char * decorators [MAX_ECFLAGS_DECORATORS ];
1165- int i ;
1166-
1167- num_decorators = decorator_names (ecflags , decorators );
1168- if (num_decorators == -1 )
1169- return NULL ;
1170-
1171- ec = rb_econv_open0 (NULL , sname , NULL , dname , ecflags & ECONV_ERROR_HANDLER_MASK );
1172- if (ec ) {
1173- for (i = 0 ; i < num_decorators ; i ++ ) {
1174- if (rb_econv_decorate_at_last (ec , decorators [i ]) == -1 ) {
1175- rb_econv_close (ec );
1176- ec = NULL ;
1177- break ;
1178- }
1179- }
1180- }
1181-
1182- if (ec ) {
1183- ec -> flags |= ecflags & ~ECONV_ERROR_HANDLER_MASK ;
1184- }
1185- return ec ; // can be NULL
1193+ return rb_econv_open_enc (NULL , sname , NULL , dname , ecflags );
11861194}
11871195
11881196static int
@@ -2755,35 +2763,7 @@ rb_econv_open_opts_enc(rb_encoding *senc, const char *source_encoding, rb_encodi
27552763rb_econv_t *
27562764rb_econv_open_opts (const char * source_encoding , const char * destination_encoding , int ecflags , VALUE opthash )
27572765{
2758- rb_econv_t * ec ;
2759- VALUE replacement ;
2760-
2761- if (NIL_P (opthash )) {
2762- replacement = Qnil ;
2763- }
2764- else {
2765- if (!RB_TYPE_P (opthash , T_HASH ) || !OBJ_FROZEN (opthash ))
2766- rb_bug ("rb_econv_open_opts called with invalid opthash" );
2767- replacement = rb_hash_aref (opthash , sym_replace );
2768- }
2769-
2770- ec = rb_econv_open (source_encoding , destination_encoding , ecflags );
2771- if (ec ) {
2772- if (!NIL_P (replacement )) {
2773- int ret ;
2774- rb_encoding * enc = rb_enc_get (replacement );
2775-
2776- ret = rb_econv_set_replacement (ec ,
2777- (const unsigned char * )RSTRING_PTR (replacement ),
2778- RSTRING_LEN (replacement ),
2779- rb_enc_name (enc ));
2780- if (ret == -1 ) {
2781- rb_econv_close (ec );
2782- ec = NULL ;
2783- }
2784- }
2785- }
2786- return ec ; // can be NULL
2766+ return rb_econv_open_opts_enc (NULL , source_encoding , NULL , destination_encoding , ecflags , opthash );
27872767}
27882768
27892769static int
@@ -4568,8 +4548,10 @@ void
45684548Init_transcode (void )
45694549{
45704550 transcoder_table = st_init_strcasetable ();
4571- fast_transcoder_table = rb_managed_id_table_new (8 ); // NOTE: size is arbitrarily chosen
4572- rb_gc_register_address (& fast_transcoder_table );
4551+ fast_transcoder_path_table = rb_managed_id_table_new (8 ); // NOTE: size is arbitrarily chosen
4552+ rb_gc_register_address (& fast_transcoder_path_table );
4553+ fast_transcoder_entry_table = rb_managed_id_table_new (8 );
4554+ rb_gc_register_address (& fast_transcoder_entry_table );
45734555
45744556 id_destination_encoding = rb_intern_const ("destination_encoding" );
45754557 id_destination_encoding_name = rb_intern_const ("destination_encoding_name" );
0 commit comments