summaryrefslogtreecommitdiffhomepage
path: root/src/enum.c
diff options
context:
space:
mode:
authorkimu_shu <[email protected]>2018-04-27 11:40:48 +0900
committerYukihiro "Matz" Matsumoto <[email protected]>2018-04-28 11:26:23 +0900
commit4b29abc565b5791d6d39b28358e2c82c17d3c500 (patch)
tree722abe1f434b5ac71958fab9650f8f7cb3c8fe49 /src/enum.c
parent84b499d02b0b612fc5c937dd40fcaa592a1f009e (diff)
downloadmruby-4b29abc565b5791d6d39b28358e2c82c17d3c500.tar.gz
mruby-4b29abc565b5791d6d39b28358e2c82c17d3c500.zip
Fix Enumerable#hash on non 32-bit integer conf.
Diffstat (limited to 'src/enum.c')
-rw-r--r--src/enum.c56
1 files changed, 55 insertions, 1 deletions
diff --git a/src/enum.c b/src/enum.c
index adb815bf1..ed3ebdd9f 100644
--- a/src/enum.c
+++ b/src/enum.c
@@ -5,10 +5,64 @@
*/
#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)
{
- mrb_define_module(mrb, "Enumerable"); /* 15.3.2 */
+ struct RClass *enumerable;
+ enumerable = mrb_define_module(mrb, "Enumerable"); /* 15.3.2 */
+ mrb_define_module_function(mrb, enumerable, "hash", enumerable_hash, MRB_ARGS_NONE());
}