summaryrefslogtreecommitdiff
path: root/ext/-test-/st/numhash/numhash.c
blob: 7e8d5d9fe2db24c89c6e436d53ee563c4eb2e95e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <ruby.h>
#include <ruby/st.h>

static void
numhash_free(void *ptr)
{
    if (ptr) st_free_table(ptr);
}

static size_t
numhash_memsize(const void *ptr)
{
    return st_memsize(ptr);
}

static const rb_data_type_t numhash_type = {
    "numhash",
    {0, numhash_free, numhash_memsize,},
    0, 0,
    RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED,
};

static VALUE
numhash_alloc(VALUE klass)
{
    return TypedData_Wrap_Struct(klass, &numhash_type, 0);
}

static VALUE
numhash_init(VALUE self)
{
    st_table *tbl = (st_table *)Check_TypedStruct(self, &numhash_type);
    if (tbl) st_free_table(tbl);
    DATA_PTR(self) = st_init_numtable();
    return self;
}

static VALUE
numhash_aref(VALUE self, VALUE key)
{
    st_data_t data;
    st_table *tbl = (st_table *)Check_TypedStruct(self, &numhash_type);
    if (!SPECIAL_CONST_P(key)) rb_raise(rb_eArgError, "not a special const");
    if (st_lookup(tbl, (st_data_t)key, &data))
        return (VALUE)data;
    return Qnil;
}

static VALUE
numhash_aset(VALUE self, VALUE key, VALUE data)
{
    st_table *tbl = (st_table *)Check_TypedStruct(self, &numhash_type);
    if (!SPECIAL_CONST_P(key)) rb_raise(rb_eArgError, "not a special const");
    if (!SPECIAL_CONST_P(data)) rb_raise(rb_eArgError, "not a special const");
    st_insert(tbl, (st_data_t)key, (st_data_t)data);
    return self;
}

static int
numhash_i(st_data_t key, st_data_t value, st_data_t arg, int _)
{
    VALUE ret;
    ret = rb_yield_values(3, (VALUE)key, (VALUE)value, (VALUE)arg);
    if (ret == Qtrue) return ST_CHECK;
    return ST_CONTINUE;
}

static VALUE
numhash_each(VALUE self)
{
    st_table *table = (st_table *)Check_TypedStruct(self, &numhash_type);
    st_data_t data = (st_data_t)self;
    return st_foreach_check(table, numhash_i, data, data) ? Qtrue : Qfalse;
}

static int
update_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
{
    VALUE ret = rb_yield_values(existing ? 2 : 1, (VALUE)*key, (VALUE)*value);
    switch (ret) {
      case Qfalse:
        return ST_STOP;
      case Qnil:
        return ST_DELETE;
      default:
        *value = ret;
        return ST_CONTINUE;
    }
}

static VALUE
numhash_update(VALUE self, VALUE key)
{
    st_table *table = (st_table *)Check_TypedStruct(self, &numhash_type);
    if (st_update(table, (st_data_t)key, update_func, 0))
        return Qtrue;
    else
        return Qfalse;
}

#if SIZEOF_LONG == SIZEOF_VOIDP
# define ST2NUM(x) ULONG2NUM(x)
#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
# define ST2NUM(x) ULL2NUM(x)
#endif

static VALUE
numhash_size(VALUE self)
{
    st_table *table = (st_table *)Check_TypedStruct(self, &numhash_type);
    return ST2NUM(table->num_entries);
}

static VALUE
numhash_delete_safe(VALUE self, VALUE key)
{
    st_table *table = (st_table *)Check_TypedStruct(self, &numhash_type);
    st_data_t val, k = (st_data_t)key;
    if (st_delete_safe(table, &k, &val, (st_data_t)self)) {
        return val;
    }
    return Qnil;
}

void
Init_numhash(void)
{
    VALUE st = rb_define_class_under(rb_define_module("Bug"), "StNumHash", rb_cObject);
    rb_define_alloc_func(st, numhash_alloc);
    rb_define_method(st, "initialize", numhash_init, 0);
    rb_define_method(st, "[]", numhash_aref, 1);
    rb_define_method(st, "[]=", numhash_aset, 2);
    rb_define_method(st, "each", numhash_each, 0);
    rb_define_method(st, "update", numhash_update, 1);
    rb_define_method(st, "size", numhash_size, 0);
    rb_define_method(st, "delete_safe", numhash_delete_safe, 1);
}