git » nmdb » commit 72ffddf

Implement Ruby bindings.

author Alberto Bertogli
2007-06-29 05:24:55 UTC
committer Alberto Bertogli
2007-06-29 05:24:55 UTC
parent 754fc49700beb93ec03f2038fe01ebfbfc134cff

Implement Ruby bindings.

Signed-off-by: Alberto Bertogli <albertito@gmail.com>

bindings/ruby/extconf.rb +13 -0
bindings/ruby/nmdb.rb +164 -0
bindings/ruby/nmdb_ll.c +220 -0

diff --git a/bindings/ruby/extconf.rb b/bindings/ruby/extconf.rb
new file mode 100644
index 0000000..2da19fb
--- /dev/null
+++ b/bindings/ruby/extconf.rb
@@ -0,0 +1,13 @@
+#!/usr/bin/env ruby
+
+require 'mkmf'
+
+if have_library("nmdb", "nmdb_init") and have_header("nmdb.h")
+then
+	dir_config("libnmdb")
+
+	create_makefile("nmdb_ll")
+else
+	puts "Can't find libnmdb"
+end
+
diff --git a/bindings/ruby/nmdb.rb b/bindings/ruby/nmdb.rb
new file mode 100644
index 0000000..608a55f
--- /dev/null
+++ b/bindings/ruby/nmdb.rb
@@ -0,0 +1,164 @@
+
+module Nmdb
+
+require "nmdb_ll"
+
+
+class NetworkException < StandardError
+end
+
+
+class GenericDB
+	attr_writer :automarshal
+
+	def initialize()
+		@db = Nmdb_ll::DB.new
+		@automarshal = true
+	end
+
+	def add_tipc_server(port = -1)
+		return @db.add_tipc_server(port)
+	end
+
+	def add_tcp_server(host, port = 26010)
+		return @db.add_tcp_server(port)
+	end
+
+	def add_udp_server(host, port = 26010)
+		return @db.add_udp_server(port)
+	end
+
+
+	def normal_set(key, val)
+		if @automarshal then
+			key = Marshal.dump(key)
+			val = Marshal.dump(val)
+		end
+		return @db.set(key, val)
+	end
+
+	def set_sync(key, val)
+		if @automarshal then
+			key = Marshal.dump(key)
+			val = Marshal.dump(val)
+		end
+		return @db.set_sync(key, val)
+	end
+
+	def cache_set(key, val)
+		if @automarshal then
+			key = Marshal.dump(key)
+			val = Marshal.dump(val)
+		end
+		return @db.cache_set(key, val)
+	end
+
+
+	def generic_get(gfunc, key)
+		if @automarshal then
+			key = Marshal.dump(key)
+		end
+		r = gfunc.call(key)
+		if r.class == String then
+			if @automarshal then
+				r = Marshal.load(r)
+			end
+			return r
+		elsif r == -1 then
+			# key not in the database
+			return nil
+		elsif r <= 2 then
+			raise NetworkException
+		else
+			# we should never get here
+			raise Exception
+		end
+	end
+
+	def normal_get(key)
+		return generic_get(@db.method(:get), key)
+	end
+
+	def cache_get(key)
+		return generic_get(@db.method(:cache_get), key)
+	end
+
+
+	def normal_delete(key)
+		if @automarshal then
+			key = Marshal.dump(key)
+		end
+		return @db.del(key)
+	end
+
+	def cache_delete(key)
+		if @automarshal then
+			key = Marshal.dump(key)
+		end
+		return @db.cache_del(key)
+	end
+
+	def delete_sync(key)
+		if @automarshal then
+			key = Marshal.dump(key)
+		end
+		return @db.del_sync(key)
+	end
+
+
+	def generic_cas(gfunc, key, old, new)
+		if @automarshal then
+			key = Marshal.dump(key)
+			old = Marshal.dump(old)
+			new = Marshal.dump(new)
+		end
+		r = gfunc.call(key, old, new)
+		if r == 0 then
+			# key not in the database
+			return nil
+		elsif r < 0 then
+			raise NetworkException
+		else
+			return r
+		end
+	end
+
+	def normal_cas(key, old, new)
+		return generic_cas(@db.method(:cas), key, old, new)
+	end
+
+	def cache_cas(key, old, new)
+		return generic_cas(@db.method(:cache_cas), key, old, new)
+	end
+end
+
+
+class DB < GenericDB
+	alias set normal_set
+	alias get normal_get
+	alias delete normal_delete
+	alias cas normal_cas
+	alias []= set
+	alias [] get
+end
+
+class Cache < GenericDB
+	alias set cache_set
+	alias get cache_get
+	alias delete cache_delete
+	alias cas cache_cas
+	alias []= set
+	alias [] get
+end
+
+class Sync < GenericDB
+	alias set set_sync
+	alias get normal_get
+	alias delete delete_sync
+	alias cas normal_cas
+	alias []= set
+	alias [] get
+end
+
+end
+
diff --git a/bindings/ruby/nmdb_ll.c b/bindings/ruby/nmdb_ll.c
new file mode 100644
index 0000000..05e7b4f
--- /dev/null
+++ b/bindings/ruby/nmdb_ll.c
@@ -0,0 +1,220 @@
+
+#include "nmdb.h"
+#include <ruby.h>
+
+static VALUE rb_mnmdb_ll;
+static VALUE rb_cDB;
+
+
+/* Functions for ruby memory management.
+ * The "mm" prefix is used by us (not enforced by Ruby) to distinguish it from
+ * the rest. */
+
+static void mm_db_mark(nmdb_t *db)
+{
+}
+
+static void mm_db_free(nmdb_t *db)
+{
+	nmdb_free(db);
+}
+
+static VALUE mm_db_allocate(VALUE class)
+{
+	nmdb_t *db = nmdb_init();
+
+	return Data_Wrap_Struct(class, mm_db_mark, mm_db_free, db);
+}
+
+
+/*
+ * DB methods
+ */
+
+VALUE m_add_tipc_server(VALUE self, VALUE port)
+{
+	nmdb_t *db;
+	Data_Get_Struct(self, nmdb_t, db);
+
+	int rv = nmdb_add_tipc_server(db, NUM2INT(port));
+	return INT2NUM(rv);
+}
+
+VALUE m_add_tcp_server(VALUE self, VALUE hostname, VALUE port)
+{
+	nmdb_t *db;
+	Data_Get_Struct(self, nmdb_t, db);
+
+	int rv = nmdb_add_tcp_server(db, StringValuePtr(hostname),
+			NUM2INT(port));
+	return INT2NUM(rv);
+}
+
+VALUE m_add_udp_server(VALUE self, VALUE hostname, VALUE port)
+{
+	nmdb_t *db;
+	Data_Get_Struct(self, nmdb_t, db);
+
+	int rv = nmdb_add_udp_server(db, StringValuePtr(hostname),
+			NUM2INT(port));
+	return INT2NUM(rv);
+}
+
+
+/* Set functions */
+typedef int (*setf_t) (nmdb_t *db,
+		const unsigned char *k, size_t ks,
+		const unsigned char *v, size_t vs);
+static VALUE generic_set(VALUE self, VALUE key, VALUE val, setf_t set_func)
+{
+	int rv;
+	unsigned char *k, *v;
+	size_t ksize, vsize;
+	nmdb_t *db;
+	Data_Get_Struct(self, nmdb_t, db);
+
+	k = rb_str2cstr(key, &ksize);
+	v = rb_str2cstr(val, &vsize);
+
+	rv = set_func(db, k, ksize, v, vsize);
+	return INT2NUM(rv);
+}
+
+static VALUE m_set(VALUE self, VALUE key, VALUE val) {
+	return generic_set(self, key, val, nmdb_set);
+}
+
+static VALUE m_set_sync(VALUE self, VALUE key, VALUE val) {
+	return generic_set(self, key, val, nmdb_set_sync);
+}
+
+static VALUE m_cache_set(VALUE self, VALUE key, VALUE val) {
+	return generic_set(self, key, val, nmdb_cache_set);
+}
+
+
+/* Get functions */
+typedef ssize_t (*getf_t) (nmdb_t *db,
+		const unsigned char *k, size_t ks,
+		unsigned char *v, size_t vs);
+VALUE generic_get(VALUE self, VALUE key, getf_t get_func)
+{
+	ssize_t rv;
+	unsigned char *k, *v;
+	size_t ksize, vsize;
+	VALUE retval;
+	nmdb_t *db;
+	Data_Get_Struct(self, nmdb_t, db);
+
+	k = rb_str2cstr(key, &ksize);
+
+	v = ALLOC_N(char, (68 * 1024));
+
+	rv = get_func(db, k, ksize, v, (68 * 1024));
+	if (rv >= 0)
+		retval = rb_str_new(v, rv);
+	else
+		/* The high level module knows and checks for this, there's no
+		 * need to make the C code more complex by raising an
+		 * exception. */
+		retval = INT2NUM(rv);
+
+	free(v);
+	return retval;
+}
+
+VALUE m_get(VALUE self, VALUE key) {
+	return generic_get(self, key, nmdb_get);
+}
+
+VALUE m_cache_get(VALUE self, VALUE key) {
+	return generic_get(self, key, nmdb_cache_get);
+}
+
+
+/* Del functions */
+typedef int (*delf_t) (nmdb_t *db, const unsigned char *k, size_t ks);
+VALUE generic_del(VALUE self, VALUE key, delf_t del_func)
+{
+	ssize_t rv;
+	unsigned char *k;
+	size_t ksize;
+	nmdb_t *db;
+	Data_Get_Struct(self, nmdb_t, db);
+
+	k = rb_str2cstr(key, &ksize);
+
+	rv = del_func(db, k, ksize);
+	return INT2NUM(rv);
+}
+
+VALUE m_del(VALUE self, VALUE key) {
+	return generic_del(self, key, nmdb_del);
+}
+
+VALUE m_del_sync(VALUE self, VALUE key) {
+	return generic_del(self, key, nmdb_del_sync);
+}
+
+VALUE m_cache_del(VALUE self, VALUE key) {
+	return generic_del(self, key, nmdb_cache_del);
+}
+
+
+/* CAS functions */
+typedef int (*casf_t) (nmdb_t *db,
+		const unsigned char *k, size_t ks,
+		const unsigned char *ov, size_t ovs,
+		const unsigned char *nv, size_t nvs);
+static VALUE generic_cas(VALUE self, VALUE key, VALUE oldval, VALUE newval,
+		casf_t cas_func)
+{
+	int rv;
+	unsigned char *k, *ov, *nv;
+	size_t ksize, ovsize, nvsize;
+	nmdb_t *db;
+	Data_Get_Struct(self, nmdb_t, db);
+
+	k = rb_str2cstr(key, &ksize);
+	ov = rb_str2cstr(oldval, &ovsize);
+	nv = rb_str2cstr(newval, &nvsize);
+
+	rv = cas_func(db, k, ksize, ov, ovsize, nv, nvsize);
+	return INT2NUM(rv);
+}
+
+static VALUE m_cas(VALUE self, VALUE key, VALUE oldval, VALUE newval) {
+	return generic_cas(self, key, oldval, newval, nmdb_cas);
+}
+
+static VALUE m_cache_cas(VALUE self, VALUE key, VALUE oldval, VALUE newval) {
+	return generic_cas(self, key, oldval, newval, nmdb_cache_cas);
+}
+
+
+/* Module initialization */
+void Init_nmdb_ll()
+{
+	rb_mnmdb_ll = rb_define_module("Nmdb_ll");
+	rb_cDB = rb_define_class_under(rb_mnmdb_ll, "DB", rb_cObject);
+	rb_define_alloc_func(rb_cDB, mm_db_allocate);
+
+	rb_define_method(rb_cDB, "add_tipc_server", m_add_tipc_server, 1);
+	rb_define_method(rb_cDB, "add_tcp_server", m_add_tcp_server, 2);
+	rb_define_method(rb_cDB, "add_udp_server", m_add_udp_server, 2);
+
+	rb_define_method(rb_cDB, "set", m_set, 2);
+	rb_define_method(rb_cDB, "set_sync", m_set_sync, 2);
+	rb_define_method(rb_cDB, "cache_set", m_cache_set, 2);
+
+	rb_define_method(rb_cDB, "get", m_get, 1);
+	rb_define_method(rb_cDB, "cache_get", m_cache_get, 1);
+
+	rb_define_method(rb_cDB, "del", m_del, 1);
+	rb_define_method(rb_cDB, "del_sync", m_del_sync, 1);
+	rb_define_method(rb_cDB, "cache_del", m_cache_del, 1);
+
+	rb_define_method(rb_cDB, "cas", m_cas, 3);
+	rb_define_method(rb_cDB, "cache_cas", m_cache_cas, 3);
+}
+