st_tableを拡張ライブラリで使う

RHGの逆襲で「st_tableを拡張で使うと便利だよ」という話を聞いて、へーと思ったので、st_tableで簡単なハッシュテーブルを実装してみた。

拡張ライブラリ

#ifdef _WIN32
__declspec(dllexport) void Init_my_hash(void);
#endif

#include "ruby.h"
#include "st.h"

static void my_hash_free(st_table *table) {
  st_free_table(table);
}

static VALUE my_hash_alloc(VALUE klass) {
  st_table *table = st_init_strtable();
  return Data_Wrap_Struct(klass, 0, -1, table);
}

static VALUE my_hash_get(VALUE self, VALUE key) {
  st_table *table;
  st_data_t value = Qnil;

  Data_Get_Struct(self, st_table, table);
  Check_Type(key, T_STRING);
  st_lookup(table, (st_data_t) StringValuePtr(key), &value);
  return value;
}

static VALUE my_hash_set(VALUE self, VALUE key, VALUE value) {
  st_table *table;

  Data_Get_Struct(self, st_table, table);
  Check_Type(key, T_STRING);
  st_insert(table, (st_data_t) StringValuePtr(key), value);
  return Qnil;
}

void Init_my_hash() {
  VALUE MyHash;

  MyHash = rb_define_class("MyHash", rb_cObject);
  rb_define_alloc_func(MyHash, my_hash_alloc);
  rb_define_method(MyHash, "[]", my_hash_get, 1);
  rb_define_method(MyHash, "[]=", my_hash_set, 2);
}

テスト

require 'my_hash'

h = MyHash.new
h['foo'] = 'bar'
h['zoo'] = :zoo

p h['foo'] #=> "bar"
p h['zoo'] #=> :zoo

拡張ライブラリ側でメモリの開放をしたいときには便利かも。
てゆーか、Ruby以外でも使えそうだなー。