Skip to content
This repository
Newer
Older
100644 453 lines (388 sloc) 10.127 kb
511dc44a »
2008-02-25 initial import
1 /************************************************
2
3 enumerator.c - provides Enumerator class
4
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
5 $Author: knu $
511dc44a »
2008-02-25 initial import
6
7 Copyright (C) 2001-2003 Akinori MUSHA
8
9 $Idaemons: /home/cvs/rb/enumerator/enumerator.c,v 1.1.1.1 2001/07/15 10:12:48 knu Exp $
10 $RoughId: enumerator.c,v 1.6 2003/07/27 11:03:24 nobu Exp $
26d0e1f6 »
2008-06-05 merge with ruby trunk r16762 + better/faster objc-like dispatcher
11 $Id: enumerator.c 16614 2008-05-26 08:49:08Z knu $
511dc44a »
2008-02-25 initial import
12
13 ************************************************/
14
15 #include "ruby/ruby.h"
16 #include "debug.h"
17
18 /*
19 * Document-class: Enumerable::Enumerator
20 *
21 * A class which provides a method `each' to be used as an Enumerable
22 * object.
23 */
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
24 VALUE rb_cEnumerator;
25 static VALUE sym_each;
511dc44a »
2008-02-25 initial import
26
27 VALUE rb_eStopIteration;
28
29 struct enumerator {
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
30 VALUE obj;
31 ID meth;
511dc44a »
2008-02-25 initial import
32 VALUE args;
33 VALUE fib;
34 VALUE dst;
35 VALUE no_next;
36 };
37
38 static void
39 enumerator_mark(void *p)
40 {
26d0e1f6 »
2008-06-05 merge with ruby trunk r16762 + better/faster objc-like dispatcher
41 #if !WITH_OBJC
511dc44a »
2008-02-25 initial import
42 struct enumerator *ptr = p;
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
43 rb_gc_mark(ptr->obj);
511dc44a »
2008-02-25 initial import
44 rb_gc_mark(ptr->args);
45 rb_gc_mark(ptr->fib);
46 rb_gc_mark(ptr->dst);
26d0e1f6 »
2008-06-05 merge with ruby trunk r16762 + better/faster objc-like dispatcher
47 #endif
511dc44a »
2008-02-25 initial import
48 }
49
50 static struct enumerator *
51 enumerator_ptr(VALUE obj)
52 {
53 struct enumerator *ptr;
54
55 Data_Get_Struct(obj, struct enumerator, ptr);
56 if (RDATA(obj)->dmark != enumerator_mark) {
57 rb_raise(rb_eTypeError,
26d0e1f6 »
2008-06-05 merge with ruby trunk r16762 + better/faster objc-like dispatcher
58 "wrong argument type %s (expected %s)",
59 rb_obj_classname(obj), rb_class2name(rb_cEnumerator));
511dc44a »
2008-02-25 initial import
60 }
61 if (!ptr) {
62 rb_raise(rb_eArgError, "uninitialized enumerator");
63 }
64 return ptr;
65 }
66
67 /*
68 * call-seq:
69 * obj.to_enum(method = :each, *args)
70 * obj.enum_for(method = :each, *args)
71 *
72 * Returns Enumerable::Enumerator.new(self, method, *args).
73 *
74 * e.g.:
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
75 *
511dc44a »
2008-02-25 initial import
76 * str = "xyz"
77 *
78 * enum = str.enum_for(:each_byte)
79 * a = enum.map {|b| '%02x' % b } #=> ["78", "79", "7a"]
80 *
81 * # protects an array from being modified
82 * a = [1, 2, 3]
83 * some_method(a.to_enum)
84 *
85 */
86 static VALUE
87 obj_to_enum(int argc, VALUE *argv, VALUE obj)
88 {
89 VALUE meth = sym_each;
90
91 if (argc > 0) {
92 --argc;
93 meth = *argv++;
94 }
95 return rb_enumeratorize(obj, meth, argc, argv);
96 }
97
98 static VALUE
99 each_slice_i(VALUE val, VALUE *memo)
100 {
101 VALUE ary = memo[0];
102 VALUE v = Qnil;
103 long size = (long)memo[1];
104
105 rb_ary_push(ary, val);
106
107 if (RARRAY_LEN(ary) == size) {
108 v = rb_yield(ary);
109 memo[0] = rb_ary_new2(size);
110 }
111
112 return v;
113 }
114
115 /*
116 * call-seq:
117 * e.each_slice(n) {...}
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
118 * e.each_slice(n)
511dc44a »
2008-02-25 initial import
119 *
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
120 * Iterates the given block for each slice of <n> elements. If no
121 * block is given, returns an enumerator.
511dc44a »
2008-02-25 initial import
122 *
123 * e.g.:
124 * (1..10).each_slice(3) {|a| p a}
125 * # outputs below
126 * [1, 2, 3]
127 * [4, 5, 6]
128 * [7, 8, 9]
129 * [10]
130 *
131 */
132 static VALUE
133 enum_each_slice(VALUE obj, VALUE n)
134 {
135 long size = NUM2LONG(n);
136 VALUE args[2], ary;
137
138 if (size <= 0) rb_raise(rb_eArgError, "invalid slice size");
139 RETURN_ENUMERATOR(obj, 1, &n);
140 args[0] = rb_ary_new2(size);
141 args[1] = (VALUE)size;
142
143 rb_block_call(obj, SYM2ID(sym_each), 0, 0, each_slice_i, (VALUE)args);
144
145 ary = args[0];
146 if (RARRAY_LEN(ary) > 0) rb_yield(ary);
147
148 return Qnil;
149 }
150
151 static VALUE
152 each_cons_i(VALUE val, VALUE *memo)
153 {
154 VALUE ary = memo[0];
155 VALUE v = Qnil;
156 long size = (long)memo[1];
157
158 if (RARRAY_LEN(ary) == size) {
159 rb_ary_shift(ary);
160 }
161 rb_ary_push(ary, val);
162 if (RARRAY_LEN(ary) == size) {
163 v = rb_yield(rb_ary_dup(ary));
164 }
165 return v;
166 }
167
168 /*
169 * call-seq:
170 * each_cons(n) {...}
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
171 * each_cons(n)
511dc44a »
2008-02-25 initial import
172 *
173 * Iterates the given block for each array of consecutive <n>
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
174 * elements. If no block is given, returns an enumerator.a
511dc44a »
2008-02-25 initial import
175 *
176 * e.g.:
177 * (1..10).each_cons(3) {|a| p a}
178 * # outputs below
179 * [1, 2, 3]
180 * [2, 3, 4]
181 * [3, 4, 5]
182 * [4, 5, 6]
183 * [5, 6, 7]
184 * [6, 7, 8]
185 * [7, 8, 9]
186 * [8, 9, 10]
187 *
188 */
189 static VALUE
190 enum_each_cons(VALUE obj, VALUE n)
191 {
192 long size = NUM2LONG(n);
193 VALUE args[2];
194
195 if (size <= 0) rb_raise(rb_eArgError, "invalid size");
196 RETURN_ENUMERATOR(obj, 1, &n);
197 args[0] = rb_ary_new2(size);
198 args[1] = (VALUE)size;
199
200 rb_block_call(obj, SYM2ID(sym_each), 0, 0, each_cons_i, (VALUE)args);
201
202 return Qnil;
203 }
204
205 static VALUE
206 enumerator_allocate(VALUE klass)
207 {
208 struct enumerator *ptr;
209 return Data_Make_Struct(klass, struct enumerator,
210 enumerator_mark, -1, ptr);
211 }
212
213 static VALUE
214 enumerator_each_i(VALUE v, VALUE enum_obj, int argc, VALUE *argv)
215 {
216 return rb_yield_values2(argc, argv);
217 }
218
219 static VALUE
220 enumerator_init(VALUE enum_obj, VALUE obj, VALUE meth, int argc, VALUE *argv)
221 {
222 struct enumerator *ptr = enumerator_ptr(enum_obj);
223
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
224 ptr->obj = obj;
225 ptr->meth = rb_to_id(meth);
f849c507 »
2008-04-01 fixing more Array regressions
226 if (argc) GC_WB(&ptr->args, rb_ary_new4(argc, argv));
511dc44a »
2008-02-25 initial import
227 ptr->fib = 0;
228 ptr->dst = Qnil;
229 ptr->no_next = Qfalse;
230
231 return enum_obj;
232 }
233
234 /*
235 * call-seq:
236 * Enumerable::Enumerator.new(obj, method = :each, *args)
237 *
238 * Creates a new Enumerable::Enumerator object, which is to be
239 * used as an Enumerable object using the given object's given
240 * method with the given arguments.
241 *
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
242 * Use of this method is not discouraged. Use Kernel#enum_for()
243 * instead.
511dc44a »
2008-02-25 initial import
244 */
245 static VALUE
246 enumerator_initialize(int argc, VALUE *argv, VALUE obj)
247 {
248 VALUE recv, meth = sym_each;
249
250 if (argc == 0)
251 rb_raise(rb_eArgError, "wrong number of argument (0 for 1)");
252 recv = *argv++;
253 if (--argc) {
254 meth = *argv++;
255 --argc;
256 }
257 return enumerator_init(obj, recv, meth, argc, argv);
258 }
259
260 /* :nodoc: */
261 static VALUE
262 enumerator_init_copy(VALUE obj, VALUE orig)
263 {
264 struct enumerator *ptr0, *ptr1;
265
266 ptr0 = enumerator_ptr(orig);
267 if (ptr0->fib) {
268 /* Fibers cannot be copied */
269 rb_raise(rb_eTypeError, "can't copy execution context");
270 }
271 ptr1 = enumerator_ptr(obj);
272
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
273 ptr1->obj = ptr0->obj;
274 ptr1->meth = ptr0->meth;
511dc44a »
2008-02-25 initial import
275 ptr1->args = ptr0->args;
276 ptr1->fib = 0;
277
278 return obj;
279 }
280
281 VALUE
282 rb_enumeratorize(VALUE obj, VALUE meth, int argc, VALUE *argv)
283 {
284 return enumerator_init(enumerator_allocate(rb_cEnumerator), obj, meth, argc, argv);
285 }
286
287 /*
288 * call-seq:
289 * enum.each {...}
290 *
291 * Iterates the given block using the object and the method specified
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
292 * in the first place. If no block is given, returns self.
511dc44a »
2008-02-25 initial import
293 *
294 */
295 static VALUE
296 enumerator_each(VALUE obj)
297 {
298 struct enumerator *e;
299 int argc = 0;
cb88a4e3 »
2008-05-11 lots of misc fixes and performance/memory usage improvements
300 const VALUE *argv = 0;
511dc44a »
2008-02-25 initial import
301
302 if (!rb_block_given_p()) return obj;
303 e = enumerator_ptr(obj);
304 if (e->args) {
305 argc = RARRAY_LEN(e->args);
306 argv = RARRAY_PTR(e->args);
307 }
a62a87ed »
2008-06-12 basic rake test rule, macruby core now builds with -Werror by default
308 return rb_block_call(e->obj, e->meth, argc, (VALUE *)argv,
26d0e1f6 »
2008-06-05 merge with ruby trunk r16762 + better/faster objc-like dispatcher
309 enumerator_each_i, (VALUE)e);
511dc44a »
2008-02-25 initial import
310 }
311
312 static VALUE
313 enumerator_with_index_i(VALUE val, VALUE *memo)
314 {
315 val = rb_yield_values(2, val, INT2FIX(*memo));
316 ++*memo;
317 return val;
318 }
319
320 /*
321 * call-seq:
322 * e.with_index {|(*args), idx| ... }
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
323 * e.with_index
511dc44a »
2008-02-25 initial import
324 *
325 * Iterates the given block for each elements with an index, which
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
326 * start from 0. If no block is given, returns an enumerator.
511dc44a »
2008-02-25 initial import
327 *
328 */
329 static VALUE
330 enumerator_with_index(VALUE obj)
331 {
26d0e1f6 »
2008-06-05 merge with ruby trunk r16762 + better/faster objc-like dispatcher
332 struct enumerator *e;
511dc44a »
2008-02-25 initial import
333 VALUE memo = 0;
334 int argc = 0;
cb88a4e3 »
2008-05-11 lots of misc fixes and performance/memory usage improvements
335 const VALUE *argv = 0;
511dc44a »
2008-02-25 initial import
336
337 RETURN_ENUMERATOR(obj, 0, 0);
26d0e1f6 »
2008-06-05 merge with ruby trunk r16762 + better/faster objc-like dispatcher
338 e = enumerator_ptr(obj);
511dc44a »
2008-02-25 initial import
339 if (e->args) {
340 argc = RARRAY_LEN(e->args);
341 argv = RARRAY_PTR(e->args);
342 }
a62a87ed »
2008-06-12 basic rake test rule, macruby core now builds with -Werror by default
343 return rb_block_call(e->obj, e->meth, argc, (VALUE *)argv,
511dc44a »
2008-02-25 initial import
344 enumerator_with_index_i, (VALUE)&memo);
345 }
346
347 static VALUE
348 next_ii(VALUE i, VALUE obj, int argc, VALUE *argv)
349 {
350 rb_fiber_yield(argc, argv);
351 return Qnil;
352 }
353
354 static VALUE
355 next_i(VALUE curr, VALUE obj)
356 {
357 struct enumerator *e = enumerator_ptr(obj);
358 VALUE rnil = Qnil;
359
360 rb_block_call(obj, rb_intern("each"), 0, 0, next_ii, obj);
361 e->no_next = Qtrue;
362 return rb_fiber_yield(1, &rnil);
363 }
364
365 static void
366 next_init(VALUE obj, struct enumerator *e)
367 {
368 VALUE curr = rb_fiber_current();
369 e->dst = curr;
370 e->fib = rb_fiber_new(next_i, obj);
371 }
372
373 /*
374 * call-seq:
375 * e.next => object
376 *
377 * Returns the next object in the enumerator, and move the internal
378 * position forward. When the position reached at the end, internal
379 * position is rewinded then StopIteration is raised.
380 *
381 * Note that enumeration sequence by next method does not affect other
382 * non-external enumeration methods, unless underlying iteration
383 * methods itself has side-effect, e.g. IO#each_line.
384 *
385 */
386
387 static VALUE
388 enumerator_next(VALUE obj)
389 {
390 struct enumerator *e = enumerator_ptr(obj);
391 VALUE curr, v;
392 curr = rb_fiber_current();
393
394 if (!e->fib || !rb_fiber_alive_p(e->fib)) {
395 next_init(obj, e);
396 }
397
398 v = rb_fiber_resume(e->fib, 1, &curr);
399 if (e->no_next) {
400 e->fib = 0;
401 e->dst = Qnil;
402 e->no_next = Qfalse;
403 rb_raise(rb_eStopIteration, "iteration reached at end");
404 }
405 return v;
406 }
407
408 /*
409 * call-seq:
410 * e.rewind => e
411 *
412 * Rewinds the enumeration sequence by the next method.
413 */
414
415 static VALUE
416 enumerator_rewind(VALUE obj)
417 {
418 struct enumerator *e = enumerator_ptr(obj);
419
420 e->fib = 0;
421 e->dst = Qnil;
422 e->no_next = Qfalse;
423 return obj;
424 }
425
426 void
427 Init_Enumerator(void)
428 {
429 rb_define_method(rb_mKernel, "to_enum", obj_to_enum, -1);
430 rb_define_method(rb_mKernel, "enum_for", obj_to_enum, -1);
431
432 rb_define_method(rb_mEnumerable, "each_slice", enum_each_slice, 1);
433 rb_define_method(rb_mEnumerable, "each_cons", enum_each_cons, 1);
434
435 rb_cEnumerator = rb_define_class_under(rb_mEnumerable, "Enumerator", rb_cObject);
436 rb_include_module(rb_cEnumerator, rb_mEnumerable);
437
438 rb_define_alloc_func(rb_cEnumerator, enumerator_allocate);
439 rb_define_method(rb_cEnumerator, "initialize", enumerator_initialize, -1);
440 rb_define_method(rb_cEnumerator, "initialize_copy", enumerator_init_copy, 1);
441 rb_define_method(rb_cEnumerator, "each", enumerator_each, 0);
26d0e1f6 »
2008-06-05 merge with ruby trunk r16762 + better/faster objc-like dispatcher
442 rb_define_method(rb_cEnumerator, "each_with_index", enumerator_with_index, 0);
511dc44a »
2008-02-25 initial import
443 rb_define_method(rb_cEnumerator, "with_index", enumerator_with_index, 0);
444 rb_define_method(rb_cEnumerator, "next", enumerator_next, 0);
445 rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0);
446
447 rb_eStopIteration = rb_define_class("StopIteration", rb_eIndexError);
448
eee9d7b4 »
2008-05-18 sync with ruby trunk r15665
449 sym_each = ID2SYM(rb_intern("each"));
511dc44a »
2008-02-25 initial import
450
451 rb_provide("enumerator.so"); /* for backward compatibility */
452 }
Something went wrong with that request. Please try again.