1 /*
2 ** range.c - Range class
3 **
4 ** See Copyright Notice in mruby.h
5 */
6 
7 #include <mruby.h>
8 #include <mruby/class.h>
9 #include <mruby/range.h>
10 #include <mruby/string.h>
11 #include <mruby/array.h>
12 
13 #define RANGE_INITIALIZED_MASK 1
14 #define RANGE_INITIALIZED(p) ((p)->flags |= RANGE_INITIALIZED_MASK)
15 #define RANGE_INITIALIZED_P(p) ((p)->flags & RANGE_INITIALIZED_MASK)
16 
17 static void
r_check(mrb_state * mrb,mrb_value a,mrb_value b)18 r_check(mrb_state *mrb, mrb_value a, mrb_value b)
19 {
20   mrb_value ans;
21   enum mrb_vtype ta;
22   enum mrb_vtype tb;
23 
24   ta = mrb_type(a);
25   tb = mrb_type(b);
26 #ifdef MRB_WITHOUT_FLOAT
27   if (ta == MRB_TT_FIXNUM && tb == MRB_TT_FIXNUM ) {
28 #else
29   if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) &&
30       (tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) {
31 #endif
32     return;
33   }
34 
35   ans =  mrb_funcall(mrb, a, "<=>", 1, b);
36   if (mrb_nil_p(ans)) {
37     /* can not be compared */
38     mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range");
39   }
40 }
41 
42 static mrb_bool
43 r_le(mrb_state *mrb, mrb_value a, mrb_value b)
44 {
45   mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
46   /* output :a < b => -1, a = b =>  0, a > b => +1 */
47 
48   if (mrb_fixnum_p(r)) {
49     mrb_int c = mrb_fixnum(r);
50     if (c == 0 || c == -1) return TRUE;
51   }
52 
53   return FALSE;
54 }
55 
56 static mrb_bool
57 r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
58 {
59   mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b);
60   /* output :a < b => -1, a = b =>  0, a > b => +1 */
61 
62   return mrb_fixnum_p(r) && mrb_fixnum(r) == 1;
63 }
64 
65 static mrb_bool
66 r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
67 {
68   mrb_value r = mrb_funcall(mrb, a, "<=>", 1, b); /* compare result */
69   /* output :a < b => -1, a = b =>  0, a > b => +1 */
70 
71   if (mrb_fixnum_p(r)) {
72     mrb_int c = mrb_fixnum(r);
73     if (c == 0 || c == 1) return TRUE;
74   }
75 
76   return FALSE;
77 }
78 
79 static void
80 range_ptr_alloc_edges(mrb_state *mrb, struct RRange *r)
81 {
82 #ifndef MRB_RANGE_EMBED
83   r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
84 #endif
85 }
86 
87 static struct RRange *
88 range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
89 {
90   r_check(mrb, beg, end);
91 
92   if (r) {
93     if (RANGE_INITIALIZED_P(r)) {
94       /* Ranges are immutable, so that they should be initialized only once. */
95       mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "`initialize' called twice");
96     }
97     else {
98       range_ptr_alloc_edges(mrb, r);
99     }
100   }
101   else {
102     r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class);
103     range_ptr_alloc_edges(mrb, r);
104   }
105 
106   RANGE_BEG(r) = beg;
107   RANGE_END(r) = end;
108   RANGE_EXCL(r) = excl;
109   RANGE_INITIALIZED(r);
110 
111   return r;
112 }
113 
114 static void
115 range_ptr_replace(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
116 {
117   range_ptr_init(mrb, r, beg, end, excl);
118   mrb_write_barrier(mrb, (struct RBasic*)r);
119 }
120 
121 /*
122  *  call-seq:
123  *     rng.first    => obj
124  *     rng.begin    => obj
125  *
126  *  Returns the first object in <i>rng</i>.
127  */
128 static mrb_value
129 range_beg(mrb_state *mrb, mrb_value range)
130 {
131   return mrb_range_beg(mrb, range);
132 }
133 
134 /*
135  *  call-seq:
136  *     rng.end    => obj
137  *     rng.last   => obj
138  *
139  *  Returns the object that defines the end of <i>rng</i>.
140  *
141  *     (1..10).end    #=> 10
142  *     (1...10).end   #=> 10
143  */
144 static mrb_value
145 range_end(mrb_state *mrb, mrb_value range)
146 {
147   return mrb_range_end(mrb, range);
148 }
149 
150 /*
151  *  call-seq:
152  *     range.exclude_end?    => true or false
153  *
154  *  Returns <code>true</code> if <i>range</i> excludes its end value.
155  */
156 static mrb_value
157 range_excl(mrb_state *mrb, mrb_value range)
158 {
159   return mrb_bool_value(mrb_range_excl_p(mrb, range));
160 }
161 
162 /*
163  *  call-seq:
164  *     Range.new(start, end, exclusive=false)    => range
165  *
166  *  Constructs a range using the given <i>start</i> and <i>end</i>. If the third
167  *  parameter is omitted or is <code>false</code>, the <i>range</i> will include
168  *  the end object; otherwise, it will be excluded.
169  */
170 static mrb_value
171 range_initialize(mrb_state *mrb, mrb_value range)
172 {
173   mrb_value beg, end;
174   mrb_bool exclusive = FALSE;
175 
176   mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
177   range_ptr_replace(mrb, mrb_range_raw_ptr(range), beg, end, exclusive);
178   return range;
179 }
180 
181 /*
182  *  call-seq:
183  *     range == obj    => true or false
184  *
185  *  Returns <code>true</code> only if
186  *  1) <i>obj</i> is a Range,
187  *  2) <i>obj</i> has equivalent beginning and end items (by comparing them with <code>==</code>),
188  *  3) <i>obj</i> has the same #exclude_end? setting as <i>rng</t>.
189  *
190  *    (0..2) == (0..2)            #=> true
191  *    (0..2) == Range.new(0,2)    #=> true
192  *    (0..2) == (0...2)           #=> false
193  */
194 static mrb_value
195 range_eq(mrb_state *mrb, mrb_value range)
196 {
197   struct RRange *rr;
198   struct RRange *ro;
199   mrb_value obj, v1, v2;
200 
201   mrb_get_args(mrb, "o", &obj);
202 
203   if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
204   if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */
205     return mrb_false_value();
206   }
207 
208   rr = mrb_range_ptr(mrb, range);
209   ro = mrb_range_ptr(mrb, obj);
210   v1 = mrb_funcall(mrb, RANGE_BEG(rr), "==", 1, RANGE_BEG(ro));
211   v2 = mrb_funcall(mrb, RANGE_END(rr), "==", 1, RANGE_END(ro));
212   if (!mrb_bool(v1) || !mrb_bool(v2) || RANGE_EXCL(rr) != RANGE_EXCL(ro)) {
213     return mrb_false_value();
214   }
215   return mrb_true_value();
216 }
217 
218 /*
219  *  call-seq:
220  *     range === obj       =>  true or false
221  *     range.member?(val)  =>  true or false
222  *     range.include?(val) =>  true or false
223  */
224 static mrb_value
225 range_include(mrb_state *mrb, mrb_value range)
226 {
227   mrb_value val;
228   struct RRange *r = mrb_range_ptr(mrb, range);
229   mrb_value beg, end;
230   mrb_bool include_p;
231 
232   mrb_get_args(mrb, "o", &val);
233 
234   beg = RANGE_BEG(r);
235   end = RANGE_END(r);
236   include_p = r_le(mrb, beg, val) &&                 /* beg <= val */
237               (RANGE_EXCL(r) ? r_gt(mrb, end, val)   /* end >  val */
238                              : r_ge(mrb, end, val)); /* end >= val */
239 
240   return mrb_bool_value(include_p);
241 }
242 
243 /* 15.2.14.4.12(x) */
244 /*
245  * call-seq:
246  *   rng.to_s   -> string
247  *
248  * Convert this range object to a printable form.
249  */
250 static mrb_value
251 range_to_s(mrb_state *mrb, mrb_value range)
252 {
253   mrb_value str, str2;
254   struct RRange *r = mrb_range_ptr(mrb, range);
255 
256   str  = mrb_obj_as_string(mrb, RANGE_BEG(r));
257   str2 = mrb_obj_as_string(mrb, RANGE_END(r));
258   str  = mrb_str_dup(mrb, str);
259   mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
260   mrb_str_cat_str(mrb, str, str2);
261 
262   return str;
263 }
264 
265 /* 15.2.14.4.13(x) */
266 /*
267  * call-seq:
268  *   rng.inspect  -> string
269  *
270  * Convert this range object to a printable form (using
271  * <code>inspect</code> to convert the start and end
272  * objects).
273  */
274 static mrb_value
275 range_inspect(mrb_state *mrb, mrb_value range)
276 {
277   mrb_value str, str2;
278   struct RRange *r = mrb_range_ptr(mrb, range);
279 
280   str  = mrb_inspect(mrb, RANGE_BEG(r));
281   str2 = mrb_inspect(mrb, RANGE_END(r));
282   str  = mrb_str_dup(mrb, str);
283   mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
284   mrb_str_cat_str(mrb, str, str2);
285 
286   return str;
287 }
288 
289 /* 15.2.14.4.14(x) */
290 /*
291  *  call-seq:
292  *     rng.eql?(obj)    -> true or false
293  *
294  *  Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
295  *  beginning and end items (by comparing them with #eql?), and has the same
296  *  #exclude_end? setting as <i>rng</i>.
297  *
298  *    (0..2).eql?(0..2)            #=> true
299  *    (0..2).eql?(Range.new(0,2))  #=> true
300  *    (0..2).eql?(0...2)           #=> false
301  */
302 static mrb_value
303 range_eql(mrb_state *mrb, mrb_value range)
304 {
305   mrb_value obj;
306   struct RRange *r, *o;
307 
308   mrb_get_args(mrb, "o", &obj);
309 
310   if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
311   if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) return mrb_false_value();
312   if (mrb_type(obj) != MRB_TT_RANGE) return mrb_false_value();
313 
314   r = mrb_range_ptr(mrb, range);
315   o = mrb_range_ptr(mrb, obj);
316   if (!mrb_eql(mrb, RANGE_BEG(r), RANGE_BEG(o)) ||
317       !mrb_eql(mrb, RANGE_END(r), RANGE_END(o)) ||
318       (RANGE_EXCL(r) != RANGE_EXCL(o))) {
319     return mrb_false_value();
320   }
321   return mrb_true_value();
322 }
323 
324 /* 15.2.14.4.15(x) */
325 static mrb_value
326 range_initialize_copy(mrb_state *mrb, mrb_value copy)
327 {
328   mrb_value src;
329   struct RRange *r;
330 
331   mrb_get_args(mrb, "o", &src);
332 
333   if (mrb_obj_equal(mrb, copy, src)) return copy;
334   if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
335     mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
336   }
337 
338   r = mrb_range_ptr(mrb, src);
339   range_ptr_replace(mrb, mrb_range_raw_ptr(copy), RANGE_BEG(r), RANGE_END(r), RANGE_EXCL(r));
340 
341   return copy;
342 }
343 
344 mrb_value
345 mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int))
346 {
347   mrb_int i, j, beg, len;
348   mrb_value result;
349   result = mrb_ary_new(mrb);
350 
351   for (i = 0; i < argc; ++i) {
352     if (mrb_fixnum_p(argv[i])) {
353       mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i])));
354     }
355     else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == 1) {
356       mrb_int const end = olen < beg + len ? olen : beg + len;
357       for (j = beg; j < end; ++j) {
358         mrb_ary_push(mrb, result, func(mrb, obj, j));
359       }
360 
361       for (; j < beg + len; ++j) {
362         mrb_ary_push(mrb, result, mrb_nil_value());
363       }
364     }
365     else {
366       mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %S", argv[i]);
367     }
368   }
369 
370   return result;
371 }
372 
373 void
374 mrb_gc_mark_range(mrb_state *mrb, struct RRange *r)
375 {
376   if (RANGE_INITIALIZED_P(r)) {
377     mrb_gc_mark_value(mrb, RANGE_BEG(r));
378     mrb_gc_mark_value(mrb, RANGE_END(r));
379   }
380 }
381 
382 MRB_API struct RRange*
383 mrb_range_ptr(mrb_state *mrb, mrb_value range)
384 {
385   struct RRange *r = mrb_range_raw_ptr(range);
386 
387   /* check for if #initialize_copy was removed [#3320] */
388   if (!RANGE_INITIALIZED_P(r)) {
389     mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
390   }
391   return r;
392 }
393 
394 MRB_API mrb_value
395 mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
396 {
397   struct RRange *r = range_ptr_init(mrb, NULL, beg, end, excl);
398   return mrb_range_value(r);
399 }
400 
401 MRB_API mrb_int
402 mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
403 {
404   mrb_int beg, end;
405   struct RRange *r;
406 
407   if (mrb_type(range) != MRB_TT_RANGE) return 0;
408   r = mrb_range_ptr(mrb, range);
409 
410   beg = mrb_int(mrb, RANGE_BEG(r));
411   end = mrb_int(mrb, RANGE_END(r));
412 
413   if (beg < 0) {
414     beg += len;
415     if (beg < 0) return 2;
416   }
417 
418   if (trunc) {
419     if (beg > len) return 2;
420     if (end > len) end = len;
421   }
422 
423   if (end < 0) end += len;
424   if (!RANGE_EXCL(r) && (!trunc || end < len)) end++;  /* include end point */
425   len = end - beg;
426   if (len < 0) len = 0;
427 
428   *begp = beg;
429   *lenp = len;
430   return 1;
431 }
432 
433 void
434 mrb_init_range(mrb_state *mrb)
435 {
436   struct RClass *r;
437 
438   r = mrb_define_class(mrb, "Range", mrb->object_class);                                /* 15.2.14 */
439   mrb->range_class = r;
440   MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE);
441 
442   mrb_define_method(mrb, r, "begin",           range_beg,             MRB_ARGS_NONE()); /* 15.2.14.4.3  */
443   mrb_define_method(mrb, r, "end",             range_end,             MRB_ARGS_NONE()); /* 15.2.14.4.5  */
444   mrb_define_method(mrb, r, "==",              range_eq,              MRB_ARGS_REQ(1)); /* 15.2.14.4.1  */
445   mrb_define_method(mrb, r, "===",             range_include,         MRB_ARGS_REQ(1)); /* 15.2.14.4.2  */
446   mrb_define_method(mrb, r, "exclude_end?",    range_excl,            MRB_ARGS_NONE()); /* 15.2.14.4.6  */
447   mrb_define_method(mrb, r, "first",           range_beg,             MRB_ARGS_NONE()); /* 15.2.14.4.7  */
448   mrb_define_method(mrb, r, "include?",        range_include,         MRB_ARGS_REQ(1)); /* 15.2.14.4.8  */
449   mrb_define_method(mrb, r, "initialize",      range_initialize,      MRB_ARGS_ANY());  /* 15.2.14.4.9  */
450   mrb_define_method(mrb, r, "last",            range_end,             MRB_ARGS_NONE()); /* 15.2.14.4.10 */
451   mrb_define_method(mrb, r, "member?",         range_include,         MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */
452   mrb_define_method(mrb, r, "to_s",            range_to_s,            MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */
453   mrb_define_method(mrb, r, "inspect",         range_inspect,         MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */
454   mrb_define_method(mrb, r, "eql?",            range_eql,             MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */
455   mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */
456 }
457