summaryrefslogtreecommitdiffhomepage
path: root/src/enum.c
blob: ed3ebdd9f8a3383cb90f26fefba77a718bff0024 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/*
** enum.c - Enumerable module
**
** See Copyright Notice in mruby.h
*/

#include <mruby.h>
#include <mruby/proc.h>

typedef struct enumerable_hash_context {
  mrb_int hash;
  mrb_int index;
} enumerable_hash_context;

static mrb_value
enumerable_hash_each(mrb_state *mrb, mrb_value self)
{
  mrb_value item;
  mrb_value closure;

  mrb_get_args(mrb, "o", &item);
  closure = mrb_proc_cfunc_env_get(mrb, 0);

  if (mrb_cptr_p(closure)) {
    mrb_value item_hash;
    enumerable_hash_context *context = (enumerable_hash_context *)mrb_cptr(closure);

    item_hash = mrb_funcall(mrb, item, "hash", 0);
    if (mrb_fixnum_p(item_hash)) {
      context->hash ^= (mrb_fixnum(item_hash) << (context->index % 16));
      ++context->index;
      return mrb_nil_value();
    }
  }

  mrb_raise(mrb, E_TYPE_ERROR, "can't calculate hash");
}

static mrb_value
enumerable_hash(mrb_state *mrb, mrb_value self)
{
  /* redefine #hash 15.3.1.3.15 */
  struct RProc *proc;
  enumerable_hash_context context;
  mrb_value closure;
  mrb_value blk;
  int ai = mrb_gc_arena_save(mrb);

  context.hash = 12347;
  context.index = 0;
  closure = mrb_cptr_value(mrb, &context);

  proc = mrb_proc_new_cfunc_with_env(mrb, enumerable_hash_each, 1, &closure);
  blk = mrb_obj_value(proc);
  mrb_funcall_with_block(mrb, self, mrb_intern_cstr(mrb, "each"), 0, NULL, blk);

  mrb_gc_arena_restore(mrb, ai);
  return mrb_fixnum_value(context.hash);
}

void
mrb_init_enumerable(mrb_state *mrb)
{
  struct RClass *enumerable;
  enumerable = mrb_define_module(mrb, "Enumerable");  /* 15.3.2 */
  mrb_define_module_function(mrb, enumerable, "hash", enumerable_hash, MRB_ARGS_NONE());
}