Well, let them break all the rules of vitality and tolerance. The MRI C API starts.
/* Name this file superhash.c. An appropriate Makefile is attached below. */ #include <ruby/ruby.h> static int key_is_in_other(VALUE key, VALUE val, VALUE data) { struct st_table *other = ((struct st_table**) data)[0]; if (st_lookup(other, key, 0)) { return ST_CONTINUE; } else { int *failed = ((int**) data)[1]; *failed = 1; return ST_STOP; } } static VALUE hash_size(VALUE hash) { if (!RHASH(hash)->ntbl) return INT2FIX(0); return INT2FIX(RHASH(hash)->ntbl->num_entries); } static VALUE same_keys(VALUE self, VALUE other) { if (CLASS_OF(other) != rb_cHash) rb_raise(rb_eArgError, "argument needs to be a hash"); if (hash_size(self) != hash_size(other)) return Qfalse; if (!RHASH(other)->ntbl && !RHASH(other)->ntbl) return Qtrue; int failed = 0; void *data[2] = { RHASH(other)->ntbl, &failed }; rb_hash_foreach(self, key_is_in_other, (VALUE) data); return failed ? Qfalse : Qtrue; } void Init_superhash(void) { rb_define_method(rb_cHash, "same_keys?", same_keys, 1); }
Here is the Makefile.
CFLAGS=-std=c99 -O2 -Wall -fPIC $(shell pkg-config ruby-1.9 --cflags) LDFLAGS=-Wl,-O1,--as-needed $(shell pkg-config ruby-1.9 --libs) superhash.so: superhash.o $(LINK.c) -shared $^ -o $@
An artificial, synthetic, and simplified example shows what follows.
require 'superhash' require 'benchmark' n = 100_000 h1 = h2 = {a:5, b:8, c:1, d:9} Benchmark.bm do |b|
source share