Skip to content

Commit 9264111

Browse files
jeremyevansnobu
andauthored
Support Marshal.{dump,load} for core Set
This was missed when adding core Set, because it's handled implicitly for T_OBJECT. Keep marshal compatibility between core Set and stdlib Set, so you can unmarshal core Set with stdlib Set and vice versa. Co-authored-by: Nobuyoshi Nakada <[email protected]>
1 parent 80a1a1b commit 9264111

File tree

4 files changed

+95
-1
lines changed

4 files changed

+95
-1
lines changed

hash.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2253,7 +2253,7 @@ rb_hash_default(int argc, VALUE *argv, VALUE hash)
22532253
* See {Hash Default}[rdoc-ref:Hash@Hash+Default].
22542254
*/
22552255

2256-
static VALUE
2256+
VALUE
22572257
rb_hash_set_default(VALUE hash, VALUE ifnone)
22582258
{
22592259
rb_hash_modify_check(hash);

internal/hash.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ struct RHash {
7272
/* hash.c */
7373
void rb_hash_st_table_set(VALUE hash, st_table *st);
7474
VALUE rb_hash_default_value(VALUE hash, VALUE key);
75+
VALUE rb_hash_set_default(VALUE hash, VALUE ifnone);
7576
VALUE rb_hash_set_default_proc(VALUE hash, VALUE proc);
7677
long rb_dbl_long_hash(double d);
7778
st_table *rb_init_identtable(void);

set.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ VALUE rb_cSet;
9999
static ID id_each_entry;
100100
static ID id_any_p;
101101
static ID id_new;
102+
static ID id_i_hash;
102103
static ID id_set_iter_lev;
103104

104105
#define RSET_INITIALIZED FL_USER1
@@ -1850,6 +1851,66 @@ set_i_hash(VALUE set)
18501851
return ST2FIX(hval);
18511852
}
18521853

1854+
/* :nodoc: */
1855+
static int
1856+
set_to_hash_i(st_data_t key, st_data_t arg)
1857+
{
1858+
rb_hash_aset((VALUE)arg, (VALUE)key, Qtrue);
1859+
return ST_CONTINUE;
1860+
}
1861+
1862+
static VALUE
1863+
set_i_to_h(VALUE set)
1864+
{
1865+
st_index_t size = RSET_SIZE(set);
1866+
VALUE hash;
1867+
if (RSET_COMPARE_BY_IDENTITY(set)) {
1868+
hash = rb_ident_hash_new_with_size(size);
1869+
}
1870+
else {
1871+
hash = rb_hash_new_with_size(size);
1872+
}
1873+
rb_hash_set_default(hash, Qfalse);
1874+
1875+
if (size == 0) return hash;
1876+
1877+
set_iter(set, set_to_hash_i, (st_data_t)hash);
1878+
return hash;
1879+
}
1880+
1881+
static VALUE
1882+
compat_dumper(VALUE set)
1883+
{
1884+
VALUE dumper = rb_class_new_instance(0, 0, rb_cObject);
1885+
rb_ivar_set(dumper, id_i_hash, set_i_to_h(set));
1886+
return dumper;
1887+
}
1888+
1889+
static int
1890+
set_i_from_hash_i(st_data_t key, st_data_t val, st_data_t set)
1891+
{
1892+
if ((VALUE)val != Qtrue) {
1893+
rb_raise(rb_eRuntimeError, "expect true as Set value: %"PRIsVALUE, rb_obj_class((VALUE)val));
1894+
}
1895+
set_i_add((VALUE)set, (VALUE)key);
1896+
return ST_CONTINUE;
1897+
}
1898+
1899+
static VALUE
1900+
set_i_from_hash(VALUE set, VALUE hash)
1901+
{
1902+
Check_Type(hash, T_HASH);
1903+
if (rb_hash_compare_by_id_p(hash)) set_i_compare_by_identity(set);
1904+
rb_hash_stlike_foreach(hash, set_i_from_hash_i, (st_data_t)set);
1905+
return set;
1906+
}
1907+
1908+
static VALUE
1909+
compat_loader(VALUE self, VALUE a)
1910+
{
1911+
return set_i_from_hash(self, rb_ivar_get(a, id_i_hash));
1912+
}
1913+
18531914
/*
18541915
* Document-class: Set
18551916
*
@@ -2068,6 +2129,7 @@ Init_Set(void)
20682129
id_each_entry = rb_intern_const("each_entry");
20692130
id_any_p = rb_intern_const("any?");
20702131
id_new = rb_intern_const("new");
2132+
id_i_hash = rb_intern_const("@hash");
20712133
id_set_iter_lev = rb_make_internal_id();
20722134

20732135
rb_define_alloc_func(rb_cSet, set_s_alloc);
@@ -2132,7 +2194,12 @@ Init_Set(void)
21322194
rb_define_method(rb_cSet, "superset?", set_i_superset, 1);
21332195
rb_define_alias(rb_cSet, ">=", "superset?");
21342196
rb_define_method(rb_cSet, "to_a", set_i_to_a, 0);
2197+
rb_define_method(rb_cSet, "to_h", set_i_to_h, 0);
21352198
rb_define_method(rb_cSet, "to_set", set_i_to_set, -1);
21362199

2200+
/* :nodoc: */
2201+
VALUE compat = rb_define_class_under(rb_cSet, "compatible", rb_cObject);
2202+
rb_marshal_define_compat(rb_cSet, compat, compat_dumper, compat_loader);
2203+
21372204
rb_provide("set.rb");
21382205
}

test/ruby/test_set.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,32 @@ class TC_Set < Test::Unit::TestCase
66
class Set2 < Set
77
end
88

9+
def test_marshal
10+
set = Set[1, 2, 3]
11+
mset = Marshal.load(Marshal.dump(set))
12+
assert_equal(set, mset)
13+
assert_equal(set.compare_by_identity?, mset.compare_by_identity?)
14+
15+
set.compare_by_identity
16+
mset = Marshal.load(Marshal.dump(set))
17+
assert_equal(set, mset)
18+
assert_equal(set.compare_by_identity?, mset.compare_by_identity?)
19+
20+
set.instance_variable_set(:@a, 1)
21+
mset = Marshal.load(Marshal.dump(set))
22+
assert_equal(set, mset)
23+
assert_equal(set.compare_by_identity?, mset.compare_by_identity?)
24+
assert_equal(1, mset.instance_variable_get(:@a))
25+
26+
old_stdlib_set_data = "\x04\bo:\bSet\x06:\n@hash}\bi\x06Ti\aTi\bTF".b
27+
set = Marshal.load(old_stdlib_set_data)
28+
assert_equal(Set[1, 2, 3], set)
29+
30+
old_stdlib_set_cbi_data = "\x04\bo:\bSet\x06:\n@hashC:\tHash}\ai\x06Ti\aTF".b
31+
set = Marshal.load(old_stdlib_set_cbi_data)
32+
assert_equal(Set[1, 2].compare_by_identity, set)
33+
end
34+
935
def test_aref
1036
assert_nothing_raised {
1137
Set[]

0 commit comments

Comments
 (0)