summaryrefslogtreecommitdiff
path: root/include/ruby/internal/core/rtypeddata.h
blob: c038e6f2b8c2025c312db89fdabb5ec09e17eb39 (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#ifndef RBIMPL_RTYPEDDATA_H                          /*-*-C++-*-vi:se ft=cpp:*/
#define RBIMPL_RTYPEDDATA_H
/**
 * @file
 * @author     Ruby developers <ruby-core@ruby-lang.org>
 * @copyright  This  file  is   a  part  of  the   programming  language  Ruby.
 *             Permission  is hereby  granted,  to  either redistribute  and/or
 *             modify this file, provided that  the conditions mentioned in the
 *             file COPYING are met.  Consult the file for details.
 * @warning    Symbols   prefixed  with   either  `RBIMPL`   or  `rbimpl`   are
 *             implementation details.   Don't take  them as canon.  They could
 *             rapidly appear then vanish.  The name (path) of this header file
 *             is also an  implementation detail.  Do not expect  it to persist
 *             at the place it is now.  Developers are free to move it anywhere
 *             anytime at will.
 * @note       To  ruby-core:  remember  that   this  header  can  be  possibly
 *             recursively included  from extension  libraries written  in C++.
 *             Do not  expect for  instance `__VA_ARGS__` is  always available.
 *             We assume C99  for ruby itself but we don't  assume languages of
 *             extension libraries. They could be written in C++98.
 * @brief      Defines struct ::RTypedData.
 */
#include "ruby/internal/config.h"

#ifdef STDC_HEADERS
# include <stddef.h>
#endif

#include "ruby/internal/assume.h"
#include "ruby/internal/attr/artificial.h"
#include "ruby/internal/attr/pure.h"
#include "ruby/internal/cast.h"
#include "ruby/internal/core/rbasic.h"
#include "ruby/internal/core/rdata.h"
#include "ruby/internal/dllexport.h"
#include "ruby/internal/error.h"
#include "ruby/internal/fl_type.h"
#include "ruby/internal/stdbool.h"
#include "ruby/internal/value_type.h"

#define HAVE_TYPE_RB_DATA_TYPE_T     1
#define HAVE_RB_DATA_TYPE_T_FUNCTION 1
#define HAVE_RB_DATA_TYPE_T_PARENT   1
#define RUBY_TYPED_DEFAULT_FREE      RUBY_DEFAULT_FREE
#define RUBY_TYPED_NEVER_FREE        RUBY_NEVER_FREE
#define RTYPEDDATA(obj)              RBIMPL_CAST((struct RTypedData *)(obj))
#define RTYPEDDATA_DATA(v)           (RTYPEDDATA(v)->data)
#define Check_TypedStruct(v, t)      \
    rb_check_typeddata(RBIMPL_CAST((VALUE)(v)), (t))

/** @cond INTERNAL_MACRO */
#define RTYPEDDATA_P                 RTYPEDDATA_P
#define RTYPEDDATA_TYPE              RTYPEDDATA_TYPE
#define RUBY_TYPED_FREE_IMMEDIATELY  RUBY_TYPED_FREE_IMMEDIATELY
#define RUBY_TYPED_FROZEN_SHAREABLE  RUBY_TYPED_FROZEN_SHAREABLE
#define RUBY_TYPED_WB_PROTECTED      RUBY_TYPED_WB_PROTECTED
#define RUBY_TYPED_PROMOTED1         RUBY_TYPED_PROMOTED1
/** @endcond */

/* bits for rb_data_type_struct::flags */
enum rbimpl_typeddata_flags {
    RUBY_TYPED_FREE_IMMEDIATELY = 1,
    RUBY_TYPED_FROZEN_SHAREABLE = RUBY_FL_SHAREABLE,
    RUBY_TYPED_WB_PROTECTED     = RUBY_FL_WB_PROTECTED, /* THIS FLAG DEPENDS ON Ruby version */
    RUBY_TYPED_PROMOTED1        = RUBY_FL_PROMOTED1     /* THIS FLAG DEPENDS ON Ruby version */
};

typedef struct rb_data_type_struct rb_data_type_t;

struct rb_data_type_struct {
    const char *wrap_struct_name;
    struct {
        RUBY_DATA_FUNC dmark;
        RUBY_DATA_FUNC dfree;
        size_t (*dsize)(const void *);
        RUBY_DATA_FUNC dcompact;
        void *reserved[1]; /* For future extension.
                              This array *must* be filled with ZERO. */
    } function;
    const rb_data_type_t *parent;
    void *data;        /* This area can be used for any purpose
                          by a programmer who define the type. */
    VALUE flags;       /* RUBY_FL_WB_PROTECTED */
};

struct RTypedData {
    struct RBasic basic;
    const rb_data_type_t *type;
    VALUE typed_flag; /* 1 or not */
    void *data;
};

RBIMPL_SYMBOL_EXPORT_BEGIN()
VALUE rb_data_typed_object_wrap(VALUE klass, void *datap, const rb_data_type_t *);
VALUE rb_data_typed_object_zalloc(VALUE klass, size_t size, const rb_data_type_t *type);
int rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t *parent);
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type);
void *rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type);
RBIMPL_SYMBOL_EXPORT_END()

#define TypedData_Wrap_Struct(klass,data_type,sval)\
  rb_data_typed_object_wrap((klass),(sval),(data_type))

#define TypedData_Make_Struct0(result, klass, type, size, data_type, sval) \
    VALUE result = rb_data_typed_object_zalloc(klass, size, data_type);    \
    (sval) = RBIMPL_CAST((type *)RTYPEDDATA_DATA(result));                  \
    RBIMPL_CAST(/*suppress unused variable warnings*/(void)(sval))

#ifdef HAVE_STMT_AND_DECL_IN_EXPR
#define TypedData_Make_Struct(klass, type, data_type, sval) \
    RB_GNUC_EXTENSION({         \
        TypedData_Make_Struct0( \
            data_struct_obj,    \
            klass,              \
            type,               \
            sizeof(type),       \
            data_type,          \
            sval);              \
        data_struct_obj;        \
    })
#else
#define TypedData_Make_Struct(klass, type, data_type, sval) \
    rb_data_typed_object_make(        \
        (klass),                      \
        (data_type),                  \
        RBIMPL_CAST((void **)&(sval)), \
        sizeof(type))
#endif

#define TypedData_Get_Struct(obj,type,data_type,sval) \
    ((sval) = RBIMPL_CAST((type *)rb_check_typeddata((obj), (data_type))))

RBIMPL_ATTR_PURE()
RBIMPL_ATTR_ARTIFICIAL()
static inline bool
rbimpl_rtypeddata_p(VALUE obj)
{
    return RTYPEDDATA(obj)->typed_flag == 1;
}

RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
static inline bool
RTYPEDDATA_P(VALUE obj)
{
#if RUBY_DEBUG
    if (RB_UNLIKELY(! RB_TYPE_P(obj, RUBY_T_DATA))) {
        Check_Type(obj, RUBY_T_DATA);
        RBIMPL_UNREACHABLE_RETURN(false);
    }
#endif

    return rbimpl_rtypeddata_p(obj);
}

RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/* :TODO: can this function be __attribute__((returns_nonnull)) or not? */
static inline const struct rb_data_type_struct *
RTYPEDDATA_TYPE(VALUE obj)
{
#if RUBY_DEBUG
    if (RB_UNLIKELY(! RTYPEDDATA_P(obj))) {
        rb_unexpected_type(obj, RUBY_T_DATA);
        RBIMPL_UNREACHABLE_RETURN(NULL);
    }
#endif

    return RTYPEDDATA(obj)->type;
}

static inline VALUE
rb_data_typed_object_make(VALUE klass, const rb_data_type_t *type, void **datap, size_t size)
{
    TypedData_Make_Struct0(result, klass, void, size, type, *datap);
    return result;
}

RBIMPL_ATTR_DEPRECATED(("by: rb_data_typed_object_wrap"))
static inline VALUE
rb_data_typed_object_alloc(VALUE klass, void *datap, const rb_data_type_t *type)
{
    return rb_data_typed_object_wrap(klass, datap, type);
}

#endif /* RBIMPL_RTYPEDDATA_H */