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