@@ -767,31 +767,39 @@ ar_free_and_clear_table(VALUE hash)
767767 HASH_ASSERT (RHASH_TRANSIENT_P (hash ) == 0 );
768768}
769769
770- static void
771- ar_try_convert_table (VALUE hash )
772- {
773- if (!RHASH_AR_TABLE_P (hash )) return ;
774-
775- const unsigned size = RHASH_AR_TABLE_SIZE (hash );
770+ void rb_st_add_direct_with_hash (st_table * tab , st_data_t key , st_data_t value , st_hash_t hash ); // st.c
776771
777- st_table * new_tab ;
778- st_index_t i ;
772+ enum ar_each_key_type {
773+ ar_each_key_copy ,
774+ ar_each_key_cmp ,
775+ ar_each_key_insert ,
776+ };
779777
780- if (size < RHASH_AR_TABLE_MAX_SIZE ) {
781- return ;
778+ static inline int
779+ ar_each_key (ar_table * ar , int max , enum ar_each_key_type type , st_data_t * dst_keys , st_table * new_tab , st_hash_t * hashes )
780+ {
781+ for (int i = 0 ; i < max ; i ++ ) {
782+ ar_table_pair * pair = & ar -> pairs [i ];
783+
784+ switch (type ) {
785+ case ar_each_key_copy :
786+ dst_keys [i ] = pair -> key ;
787+ break ;
788+ case ar_each_key_cmp :
789+ if (dst_keys [i ] != pair -> key ) return 1 ;
790+ break ;
791+ case ar_each_key_insert :
792+ if (UNDEF_P (pair -> key )) continue ; // deleted entry
793+ rb_st_add_direct_with_hash (new_tab , pair -> key , pair -> val , hashes [i ]);
794+ break ;
795+ }
782796 }
783797
784- new_tab = st_init_table_with_size (& objhash , size * 2 );
785-
786- for (i = 0 ; i < RHASH_AR_TABLE_MAX_BOUND ; i ++ ) {
787- ar_table_pair * pair = RHASH_AR_TABLE_REF (hash , i );
788- st_add_direct (new_tab , pair -> key , pair -> val );
789- }
790- ar_free_and_clear_table (hash );
791- RHASH_ST_TABLE_SET (hash , new_tab );
792- return ;
798+ return 0 ;
793799}
794800
801+
802+
795803static st_table *
796804ar_force_convert_table (VALUE hash , const char * file , int line )
797805{
@@ -802,22 +810,32 @@ ar_force_convert_table(VALUE hash, const char *file, int line)
802810 }
803811
804812 if (RHASH_AR_TABLE (hash )) {
805- unsigned i , bound = RHASH_AR_TABLE_BOUND (hash );
806-
807- #if defined(RHASH_CONVERT_TABLE_DEBUG ) && RHASH_CONVERT_TABLE_DEBUG
808- rb_obj_info_dump (hash );
809- fprintf (stderr , "force_convert: %s:%d\n" , file , line );
810- RB_DEBUG_COUNTER_INC (obj_hash_force_convert );
811- #endif
813+ ar_table * ar = RHASH_AR_TABLE (hash );
814+ st_hash_t hashes [RHASH_AR_TABLE_MAX_SIZE ];
815+ unsigned int bound , size ;
816+
817+ // prepare hash values
818+ do {
819+ st_data_t keys [RHASH_AR_TABLE_MAX_SIZE ];
820+ bound = RHASH_AR_TABLE_BOUND (hash );
821+ size = RHASH_AR_TABLE_SIZE (hash );
822+ ar_each_key (ar , bound , ar_each_key_copy , keys , NULL , NULL );
823+
824+ for (unsigned int i = 0 ; i < bound ; i ++ ) {
825+ // do_hash calls #hash method and it can modify hash object
826+ hashes [i ] = UNDEF_P (keys [i ]) ? 0 : ar_do_hash (keys [i ]);
827+ }
812828
813- new_tab = st_init_table_with_size (& objhash , RHASH_AR_TABLE_SIZE (hash ));
829+ // check if modified
830+ if (UNLIKELY (!RHASH_AR_TABLE_P (hash ))) return RHASH_ST_TABLE (hash );
831+ if (UNLIKELY (RHASH_AR_TABLE_BOUND (hash ) != bound )) continue ;
832+ if (UNLIKELY (ar_each_key (ar , bound , ar_each_key_cmp , keys , NULL , NULL ))) continue ;
833+ } while (0 );
814834
815- for (i = 0 ; i < bound ; i ++ ) {
816- if (ar_cleared_entry (hash , i )) continue ;
817835
818- ar_table_pair * pair = RHASH_AR_TABLE_REF ( hash , i );
819- st_add_direct ( new_tab , pair -> key , pair -> val );
820- }
836+ // make st
837+ new_tab = st_init_table_with_size ( & objhash , size );
838+ ar_each_key ( ar , bound , ar_each_key_insert , NULL , new_tab , hashes );
821839 ar_free_and_clear_table (hash );
822840 }
823841 else {
@@ -1724,7 +1742,7 @@ rb_hash_stlike_update(VALUE hash, st_data_t key, st_update_callback_func *func,
17241742 if (RHASH_AR_TABLE_P (hash )) {
17251743 int result = ar_update (hash , key , func , arg );
17261744 if (result == -1 ) {
1727- ar_try_convert_table (hash );
1745+ ar_force_convert_table (hash , __FILE__ , __LINE__ );
17281746 }
17291747 else {
17301748 return result ;
@@ -4801,7 +4819,7 @@ rb_hash_add_new_element(VALUE hash, VALUE key, VALUE val)
48014819 if (ret != -1 ) {
48024820 return ret ;
48034821 }
4804- ar_try_convert_table (hash );
4822+ ar_force_convert_table (hash , __FILE__ , __LINE__ );
48054823 }
48064824 tbl = RHASH_TBL_RAW (hash );
48074825 return st_update (tbl , (st_data_t )key , add_new_i , (st_data_t )args );
0 commit comments