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