Coverage Report

Created: 2025-12-22 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mruby/src/etc.c
Line
Count
Source
1
/*
2
** etc.c
3
**
4
** See Copyright Notice in mruby.h
5
*/
6
7
#include <mruby.h>
8
#include <mruby/string.h>
9
#include <mruby/data.h>
10
#include <mruby/class.h>
11
#include <mruby/numeric.h>
12
#include <mruby/internal.h>
13
14
/*
15
 * Allocates an RData structure, initializes it with the given pointer and type,
16
 * and assigns it to the given class.
17
 */
18
MRB_API struct RData*
19
mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb_data_type *type)
20
3.01k
{
21
3.01k
  struct RData *data = MRB_OBJ_ALLOC(mrb, MRB_TT_CDATA, klass);
22
23
3.01k
  data->data = ptr;
24
3.01k
  data->type = type;
25
26
3.01k
  return data;
27
3.01k
}
28
29
/*
30
 * Checks if the given mrb_value is a data object (MRB_TT_CDATA) and if its
31
 * mrb_data_type matches the provided type.
32
 * Raises an error if the checks fail.
33
 */
34
MRB_API void
35
mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
36
164k
{
37
164k
  if (!mrb_data_p(obj)) {
38
0
    mrb_check_type(mrb, obj, MRB_TT_CDATA);
39
0
  }
40
164k
  if (DATA_TYPE(obj) != type) {
41
0
    const mrb_data_type *t2 = DATA_TYPE(obj);
42
43
0
    if (t2) {
44
0
      mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)",
45
0
                 t2->struct_name, type->struct_name);
46
0
    }
47
0
    else {
48
0
      mrb_raisef(mrb, E_TYPE_ERROR, "uninitialized %t (expected %s)",
49
0
                 obj, type->struct_name);
50
0
    }
51
0
  }
52
164k
}
53
54
/*
55
 * Checks if the given mrb_value is a data object and if its mrb_data_type
56
 * matches the provided type.
57
 * Returns a pointer to the data if the checks pass, otherwise returns NULL.
58
 */
59
MRB_API void*
60
mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
61
354
{
62
354
  if (!mrb_data_p(obj)) {
63
354
    return NULL;
64
354
  }
65
0
  if (DATA_TYPE(obj) != type) {
66
0
    return NULL;
67
0
  }
68
0
  return DATA_PTR(obj);
69
0
}
70
71
/*
72
 * Retrieves a pointer to the data within a data object.
73
 * Calls `mrb_data_check_type` to ensure the object is of the correct type,
74
 * raising an error if the type check fails.
75
 */
76
MRB_API void*
77
mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
78
164k
{
79
164k
  mrb_data_check_type(mrb, obj, type);
80
164k
  return DATA_PTR(obj);
81
164k
}
82
83
/*
84
 * Converts an object to a symbol.
85
 * If the object is already a symbol, it is returned directly.
86
 * If the object is a string, it is interned to a symbol.
87
 * Otherwise, a type error is raised.
88
 */
89
MRB_API mrb_sym
90
mrb_obj_to_sym(mrb_state *mrb, mrb_value name)
91
383k
{
92
383k
  if (mrb_symbol_p(name)) return mrb_symbol(name);
93
2
  if (mrb_string_p(name)) return mrb_intern_str(mrb, name);
94
2
  mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a symbol nor a string", name);
95
0
  return 0;  /* not reached */
96
2
}
97
98
#if !defined(MRB_NO_FLOAT) && !defined(MRB_NAN_BOXING)
99
static mrb_int
100
mrb_float_id(mrb_float f)
101
0
{
102
  /* normalize -0.0 to 0.0 */
103
0
  if (f == 0) f = 0.0;
104
0
  return (mrb_int)mrb_byte_hash((uint8_t*)&f, sizeof(f));
105
0
}
106
#endif
107
108
/*
109
 * Returns a unique identifier (mrb_int) for the given object.
110
 * The method of generating the ID varies based on the object's type and
111
 * boxing model (NaN boxing, word boxing, or no boxing).
112
 */
113
MRB_API mrb_int
114
mrb_obj_id(mrb_value obj)
115
7.05k
{
116
#if defined(MRB_NAN_BOXING)
117
#ifdef MRB_INT64
118
  return obj.u;
119
#else
120
  uint64_t u = obj.u;
121
  return (mrb_int)(u>>32)^u;
122
#endif
123
#elif defined(MRB_WORD_BOXING)
124
7.05k
  if (!mrb_immediate_p(obj)) {
125
6.16k
    if (mrb_integer_p(obj)) return mrb_integer(obj);
126
6.16k
#ifndef MRB_NO_FLOAT
127
6.16k
    if (mrb_float_p(obj)) {
128
0
      return mrb_float_id(mrb_float(obj));
129
0
    }
130
6.16k
#endif
131
6.16k
  }
132
7.05k
  return (mrb_int)obj.w;
133
#else  /* MRB_NO_BOXING */
134
135
#define MakeID(p,t) (mrb_int)(((intptr_t)(p))^(t))
136
137
  enum mrb_vtype tt = mrb_type(obj);
138
139
  switch (tt) {
140
  case MRB_TT_FREE:
141
  case MRB_TT_UNDEF:
142
    return MakeID(0, tt); /* should not happen */
143
  case MRB_TT_FALSE:
144
    if (mrb_nil_p(obj))
145
      return MakeID(4, tt);
146
    else
147
      return MakeID(0, tt);
148
  case MRB_TT_TRUE:
149
    return MakeID(2, tt);
150
  case MRB_TT_SYMBOL:
151
    return MakeID(mrb_symbol(obj), tt);
152
  case MRB_TT_INTEGER:
153
    return MakeID(mrb_integer(obj), tt);
154
#ifndef MRB_NO_FLOAT
155
  case MRB_TT_FLOAT:
156
    return MakeID(mrb_float_id(mrb_float(obj)), tt);
157
#endif
158
  case MRB_TT_STRING:
159
  case MRB_TT_OBJECT:
160
  case MRB_TT_CLASS:
161
  case MRB_TT_MODULE:
162
  case MRB_TT_ICLASS:
163
  case MRB_TT_SCLASS:
164
  case MRB_TT_PROC:
165
  case MRB_TT_ARRAY:
166
  case MRB_TT_HASH:
167
  case MRB_TT_RANGE:
168
  case MRB_TT_EXCEPTION:
169
  case MRB_TT_CDATA:
170
  case MRB_TT_ISTRUCT:
171
  default:
172
    return MakeID(mrb_ptr(obj), tt);
173
  }
174
#endif
175
7.05k
}
176
177
#ifdef MRB_WORD_BOXING
178
#ifndef MRB_NO_FLOAT
179
/*
180
 * Boxes a `mrb_float` into an `mrb_value` using word boxing.
181
 * - If `MRB_WORDBOX_NO_FLOAT_TRUNCATE` is defined, it allocates a new
182
 *   RFloat object on the heap.
183
 * - If `MRB_64BIT` and `MRB_USE_FLOAT32` are defined, it stores the float
184
 *   in the lower bits of the word, shifted and tagged.
185
 * - Otherwise, it stores the float directly in the word, tagged.
186
 */
187
MRB_API mrb_value
188
mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
189
618k
{
190
618k
  union mrb_value_ v;
191
192
#ifdef MRB_WORDBOX_NO_FLOAT_TRUNCATE
193
  v.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class);
194
  v.fp->f = f;
195
  v.bp->frozen = 1;
196
#elif defined(MRB_64BIT) && defined(MRB_USE_FLOAT32)
197
  v.w = 0;
198
  v.f = f;
199
  v.w = (v.w<<2) | 2;
200
#else
201
618k
  v.f = f;
202
618k
  v.w = (v.w & ~3) | 2;
203
618k
#endif
204
618k
  return v.value;
205
618k
}
206
207
208
#ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE
209
/*
210
 * Unboxes an `mrb_value` to an `mrb_float` when word boxing is used and
211
 * float truncation (`MRB_WORDBOX_NO_FLOAT_TRUNCATE`) is not disabled.
212
 * The function extracts the float value from the `mrb_value`'s union
213
 * representation (`u.value`).
214
 * - If `MRB_64BIT` and `MRB_USE_FLOAT32` are defined, the word (`u.w`)
215
 *   is right-shifted by 2 bits to retrieve the float.
216
 * - Otherwise, the lower 2 bits of the word (`u.w`) are cleared to
217
 *   retrieve the float.
218
 */
219
MRB_API mrb_float
220
mrb_word_boxing_value_float(mrb_value v)
221
684k
{
222
684k
  union mrb_value_ u;
223
684k
  u.value = v;
224
#if defined(MRB_64BIT) && defined(MRB_USE_FLOAT32)
225
  u.w >>= 2;
226
#else
227
684k
  u.w &= ~3;
228
684k
#endif
229
684k
  return u.f;
230
684k
}
231
#endif
232
#endif  /* MRB_NO_FLOAT */
233
234
/*
235
 * Boxes a C pointer (void*) into an `mrb_value` using word boxing.
236
 * It allocates an `RCptr` object, sets its internal pointer `p` to the
237
 * given C pointer, and then sets the `mrb_value` to this `RCptr` object.
238
 */
239
MRB_API mrb_value
240
mrb_word_boxing_cptr_value(mrb_state *mrb, void *p)
241
2.68k
{
242
2.68k
  struct RCptr *cptr = MRB_OBJ_ALLOC(mrb, MRB_TT_CPTR, mrb->object_class);
243
2.68k
  mrb_value v;
244
245
2.68k
  SET_OBJ_VALUE(v, cptr);
246
2.68k
  cptr->p = p;
247
2.68k
  return v;
248
2.68k
}
249
#endif  /* MRB_WORD_BOXING */
250
251
#if defined(MRB_WORD_BOXING) || (defined(MRB_NAN_BOXING) && defined(MRB_INT64))
252
/*
253
 * Boxes an `mrb_int` into an `mrb_value`.
254
 * If the integer `n` can be represented as a fixnum (checked by `FIXABLE(n)`),
255
 * it returns a fixnum-tagged `mrb_value`. Otherwise, it allocates an
256
 * `RInteger` object on the heap, stores `n` in it, marks the object as
257
 * frozen, and returns an object-tagged `mrb_value`.
258
 * This function is used when word boxing is enabled or when NaN boxing is
259
 * enabled for 64-bit integers.
260
 */
261
MRB_API mrb_value
262
mrb_boxing_int_value(mrb_state *mrb, mrb_int n)
263
3.96M
{
264
3.96M
  if (FIXABLE(n)) return mrb_fixnum_value(n);
265
16.0k
  else {
266
16.0k
    mrb_value v;
267
16.0k
    struct RInteger *p = (struct RInteger*)mrb_obj_alloc(mrb, MRB_TT_INTEGER, mrb->integer_class);
268
16.0k
    p->i = n;
269
16.0k
    p->frozen = 1;
270
16.0k
    SET_OBJ_VALUE(v, p);
271
16.0k
    return v;
272
16.0k
  }
273
3.96M
}
274
#endif
275
276
#if defined _MSC_VER && _MSC_VER < 1900
277
278
#ifndef va_copy
279
static void
280
mrb_msvc_va_copy(va_list *dest, va_list src)
281
{
282
  *dest = src;
283
}
284
#define va_copy(dest, src) mrb_msvc_va_copy(&(dest), src)
285
#endif
286
287
MRB_API int
288
mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg)
289
{
290
  int cnt;
291
  va_list argcp;
292
  va_copy(argcp, arg);
293
  if (n == 0 || (cnt = _vsnprintf_s(s, n, _TRUNCATE, format, argcp)) < 0) {
294
    cnt = _vscprintf(format, arg);
295
  }
296
  va_end(argcp);
297
  return cnt;
298
}
299
300
MRB_API int
301
mrb_msvc_snprintf(char *s, size_t n, const char *format, ...)
302
{
303
  va_list arg;
304
  va_start(arg, format);
305
306
  int ret = mrb_msvc_vsnprintf(s, n, format, arg);
307
  va_end(arg);
308
  return ret;
309
}
310
311
#endif  /* defined _MSC_VER && _MSC_VER < 1900 */