@@ -716,18 +716,18 @@ num_members(VALUE klass)
716716struct struct_hash_set_arg {
717717 VALUE self ;
718718 VALUE unknown_keywords ;
719- VALUE found_keywords ;
719+ VALUE missing_keywords ;
720+ long missing_count ;
720721};
721722
722- static int rb_struct_pos (VALUE s , VALUE * name );
723+ static int rb_struct_pos (VALUE s , VALUE * name , bool name_only );
724+ static VALUE deconstruct_keys (VALUE s , VALUE keys , bool name_only );
725+ static int rb_struct_pos (VALUE s , VALUE * name , bool name_only );
723726
724727static int
725- struct_hash_set_i (VALUE key , VALUE val , VALUE arg )
728+ struct_hash_aset (VALUE key , VALUE val , struct struct_hash_set_arg * args , bool name_only )
726729{
727- struct struct_hash_set_arg * args = (struct struct_hash_set_arg * )arg ;
728- VALUE klass = rb_obj_class (args -> self );
729- VALUE members = struct_ivar_get (klass , id_members );
730- int i = rb_struct_pos (args -> self , & key );
730+ int i = rb_struct_pos (args -> self , & key , name_only );
731731 if (i < 0 ) {
732732 if (NIL_P (args -> unknown_keywords )) {
733733 args -> unknown_keywords = rb_ary_new ();
@@ -737,13 +737,15 @@ struct_hash_set_i(VALUE key, VALUE val, VALUE arg)
737737 else {
738738 rb_struct_modify (args -> self );
739739 RSTRUCT_SET_RAW (args -> self , i , val );
740-
741- if (NIL_P (args -> found_keywords )) {
742- args -> found_keywords = rb_hash_new ();
743- }
744- VALUE member = rb_ary_entry (members , i );
745- rb_hash_aset (args -> found_keywords , member , Qtrue );
746740 }
741+ return i ;
742+ }
743+
744+ static int
745+ struct_hash_set_i (VALUE key , VALUE val , VALUE arg )
746+ {
747+ struct struct_hash_set_arg * args = (struct struct_hash_set_arg * )arg ;
748+ struct_hash_aset (key , val , args , false);
747749 return ST_CONTINUE ;
748750}
749751
@@ -776,13 +778,13 @@ rb_struct_initialize_m(int argc, const VALUE *argv, VALUE self)
776778 break ;
777779 }
778780 if (keyword_init ) {
779- struct struct_hash_set_arg arg ;
781+ struct struct_hash_set_arg arg = {
782+ .self = self ,
783+ .unknown_keywords = Qnil ,
784+ };
780785 rb_mem_clear ((VALUE * )RSTRUCT_CONST_PTR (self ), n );
781- arg .self = self ;
782- arg .unknown_keywords = Qnil ;
783- arg .found_keywords = Qnil ;
784786 rb_hash_foreach (argv [0 ], struct_hash_set_i , (VALUE )& arg );
785- if (arg .unknown_keywords != Qnil ) {
787+ if (UNLIKELY (! NIL_P ( arg .unknown_keywords )) ) {
786788 rb_raise (rb_eArgError , "unknown keywords: %s" ,
787789 RSTRING_PTR (rb_ary_join (arg .unknown_keywords , rb_str_new2 (", " ))));
788790 }
@@ -1124,6 +1126,12 @@ rb_struct_to_h(VALUE s)
11241126 */
11251127static VALUE
11261128rb_struct_deconstruct_keys (VALUE s , VALUE keys )
1129+ {
1130+ return deconstruct_keys (s , keys , false);
1131+ }
1132+
1133+ static VALUE
1134+ deconstruct_keys (VALUE s , VALUE keys , bool name_only )
11271135{
11281136 VALUE h ;
11291137 long i ;
@@ -1143,7 +1151,7 @@ rb_struct_deconstruct_keys(VALUE s, VALUE keys)
11431151 h = rb_hash_new_with_size (RARRAY_LEN (keys ));
11441152 for (i = 0 ; i < RARRAY_LEN (keys ); i ++ ) {
11451153 VALUE key = RARRAY_AREF (keys , i );
1146- int i = rb_struct_pos (s , & key );
1154+ int i = rb_struct_pos (s , & key , name_only );
11471155 if (i < 0 ) {
11481156 return h ;
11491157 }
@@ -1171,15 +1179,15 @@ rb_struct_init_copy(VALUE copy, VALUE s)
11711179}
11721180
11731181static int
1174- rb_struct_pos (VALUE s , VALUE * name )
1182+ rb_struct_pos (VALUE s , VALUE * name , bool name_only )
11751183{
11761184 long i ;
11771185 VALUE idx = * name ;
11781186
11791187 if (SYMBOL_P (idx )) {
11801188 return struct_member_pos (s , idx );
11811189 }
1182- else if (RB_TYPE_P (idx , T_STRING )) {
1190+ else if (name_only || RB_TYPE_P (idx , T_STRING )) {
11831191 idx = rb_check_symbol (name );
11841192 if (NIL_P (idx )) return -1 ;
11851193 return struct_member_pos (s , idx );
@@ -1251,7 +1259,7 @@ invalid_struct_pos(VALUE s, VALUE idx)
12511259VALUE
12521260rb_struct_aref (VALUE s , VALUE idx )
12531261{
1254- int i = rb_struct_pos (s , & idx );
1262+ int i = rb_struct_pos (s , & idx , false );
12551263 if (i < 0 ) invalid_struct_pos (s , idx );
12561264 return RSTRUCT_GET_RAW (s , i );
12571265}
@@ -1289,26 +1297,26 @@ rb_struct_aref(VALUE s, VALUE idx)
12891297VALUE
12901298rb_struct_aset (VALUE s , VALUE idx , VALUE val )
12911299{
1292- int i = rb_struct_pos (s , & idx );
1300+ int i = rb_struct_pos (s , & idx , false );
12931301 if (i < 0 ) invalid_struct_pos (s , idx );
12941302 rb_struct_modify (s );
12951303 RSTRUCT_SET_RAW (s , i , val );
12961304 return val ;
12971305}
12981306
12991307FUNC_MINIMIZED (VALUE rb_struct_lookup (VALUE s , VALUE idx ));
1300- NOINLINE (static VALUE rb_struct_lookup_default (VALUE s , VALUE idx , VALUE notfound ));
1308+ NOINLINE (static VALUE rb_struct_lookup_default (VALUE s , VALUE idx , VALUE notfound , bool name_only ));
13011309
13021310VALUE
13031311rb_struct_lookup (VALUE s , VALUE idx )
13041312{
1305- return rb_struct_lookup_default (s , idx , Qnil );
1313+ return rb_struct_lookup_default (s , idx , Qnil , false );
13061314}
13071315
13081316static VALUE
1309- rb_struct_lookup_default (VALUE s , VALUE idx , VALUE notfound )
1317+ rb_struct_lookup_default (VALUE s , VALUE idx , VALUE notfound , bool name_only )
13101318{
1311- int i = rb_struct_pos (s , & idx );
1319+ int i = rb_struct_pos (s , & idx , name_only );
13121320 if (i < 0 ) return notfound ;
13131321 return RSTRUCT_GET_RAW (s , i );
13141322}
@@ -1744,6 +1752,21 @@ rb_data_define(VALUE super, ...)
17441752 return klass ;
17451753}
17461754
1755+ static int
1756+ data_hash_set_i (VALUE key , VALUE val , VALUE arg )
1757+ {
1758+ struct struct_hash_set_arg * args = (struct struct_hash_set_arg * )arg ;
1759+ int i = struct_hash_aset (key , val , args , true);
1760+ if (i >= 0 && args -> missing_count > 0 ) {
1761+ VALUE k = RARRAY_AREF (args -> missing_keywords , i );
1762+ if (!NIL_P (k )) {
1763+ RARRAY_ASET (args -> missing_keywords , i , Qnil );
1764+ args -> missing_count -- ;
1765+ }
1766+ }
1767+ return ST_CONTINUE ;
1768+ }
1769+
17471770/*
17481771 * call-seq:
17491772 * DataClass::members -> array_of_symbols
@@ -1827,24 +1850,31 @@ rb_data_initialize_m(int argc, const VALUE *argv, VALUE self)
18271850 rb_error_arity (argc , 0 , 0 );
18281851 }
18291852
1830- struct struct_hash_set_arg arg ;
1853+ VALUE missing = rb_ary_dup (members );
1854+ RBASIC_CLEAR_CLASS (missing );
1855+ struct struct_hash_set_arg arg = {
1856+ .self = self ,
1857+ .unknown_keywords = Qnil ,
1858+ .missing_keywords = missing ,
1859+ .missing_count = (long )num_members ,
1860+ };
18311861 rb_mem_clear ((VALUE * )RSTRUCT_CONST_PTR (self ), num_members );
1832- arg .self = self ;
1833- arg .unknown_keywords = Qnil ;
1834- arg .found_keywords = Qnil ;
1835- rb_hash_foreach (argv [0 ], struct_hash_set_i , (VALUE )& arg );
1862+ rb_hash_foreach (argv [0 ], data_hash_set_i , (VALUE )& arg );
18361863 // Freeze early before potentially raising, so that we don't leave an
18371864 // unfrozen copy on the heap, which could get exposed via ObjectSpace.
18381865 OBJ_FREEZE (self );
1839- if (arg .found_keywords != Qnil ) {
1840- VALUE missing = rb_ary_diff ( members , rb_hash_keys ( arg . found_keywords ) );
1841- if (RARRAY_LEN (missing ) > 0 ) {
1842- rb_exc_raise ( rb_keyword_error_new ( " missing" , missing ) );
1843- }
1866+ if (UNLIKELY ( arg .missing_count > 0 ) ) {
1867+ rb_ary_compact_bang ( missing );
1868+ RUBY_ASSERT (RARRAY_LEN (missing ) == arg . missing_count , "missing_count=%ld but %ld" , arg . missing_count , RARRAY_LEN ( missing ));
1869+ RBASIC_SET_CLASS_RAW ( missing , rb_cArray );
1870+ rb_exc_raise ( rb_keyword_error_new ( "missing" , missing ));
18441871 }
1845- if (arg .unknown_keywords != Qnil ) {
1846- rb_exc_raise (rb_keyword_error_new ("unknown" , arg .unknown_keywords ));
1872+ VALUE unknown_keywords = arg .unknown_keywords ;
1873+ if (UNLIKELY (!NIL_P (unknown_keywords ))) {
1874+ RBASIC_SET_CLASS_RAW (unknown_keywords , rb_cArray );
1875+ rb_exc_raise (rb_keyword_error_new ("unknown" , unknown_keywords ));
18471876 }
1877+
18481878 return Qnil ;
18491879}
18501880
@@ -2099,7 +2129,11 @@ rb_data_inspect(VALUE s)
20992129 * end
21002130 */
21012131
2102- #define rb_data_deconstruct_keys rb_struct_deconstruct_keys
2132+ static VALUE
2133+ rb_data_deconstruct_keys (VALUE s , VALUE keys )
2134+ {
2135+ return deconstruct_keys (s , keys , true);
2136+ }
21032137
21042138/*
21052139 * Document-class: Struct
0 commit comments