text: 
  //metadoc TokyoCabinet category Databases
  //metadoc TokyoCabinet copyright Steve Dekorte 2002
  //metadoc TokyoCabinet license BSD revised
  /*metadoc TokyoCabinet description
  An ordered key/value database that supports transactions and arbitrary key and value sizes.
  */
  
  #include "IoTokyoCabinet.h"
  
  #include <tcutil.h>
  #include <tcbdb.h>
  #include <stdlib.h>
  #include <stdbool.h>
  #include <stdint.h>
  
  #include "IoObject.h"
  #include "IoState.h"
  #include "IoSeq.h"
  #include "IoState.h"
  #include "IoNumber.h"
  
  #define TokyoCabinet(self) ((TCBDB *)(IoObject_dataPointer(self)))
  
  static const char *protoId = "TokyoCabinet";
  
  static int pathCompare(const char *p1, const char *p2, char sepChar, void *op)
  {
  	int i;
  	int len1 = strlen(p1);
  	int len2 = strlen(p2);
  	int len = len1 < len2 ? len1 : len2;
  
  	for (i = 0; i < len; i ++)
  	{
  		char c1 = p1[i];
  		char c2 = p2[i];
  
  		if (c1 == sepChar && c2 != sepChar)
  		{
  			return -1;
  		}
  
  
  		if (c2 == sepChar && c1 != sepChar)
  		{
  			return 1;
  		}
  
  		if (c1 > c2) return 1;
  		if (c1 < c2) return -1;
  	}
  
  	if (len1 > len2) return 1;
  	if (len1 < len2) return -1;
  
  	return 0;
  }
  
  /*
  static int comparePathComponent(const char *p1, const char *p2, char sepChar, int *size)
  {
  	char *b1 = strchr(p1 + i, sepChar);
  	char *b2 = strchr(p2 + i, sepChar);
  	int len1 = b1 ? b1 - p1 : strlen(p1);
  	int len2 = b2 ? b2 - p2 : strlen(p2);
  
  	if (len1 && isdigit(p1[0]))
  	{
  		return strtod(p1, p1 + len1) -
  	}
  
  	if (len1 > len2) return 1;
  	if (len1 < len2) return -1;
  	if (len1 == 0)   return 0;
  
  	return strcmpn(p1, p2, len1);
  }
  
  static int pathCompare(const char *p1, const char *p2, char sepChar)
  {
  	int i;
  	int len1 = strlen(p1);
  	int len2 = strlen(p2);
  	//int len = len1 < len2 ? len1 : len2;
  
  
  	char *b1 = strchr(p1 + i, '/');
  	char *b2 = strchr(p2 + i, '/');
  
  	int blen1 = p1 - b1;
  
  
  	if (len1 > len2) return 1;
  	if (len1 < len2) return -1;
  
  	return 0;
  }
  */
  
  static int pathCompareFunc(const char *aptr, int asiz, const char *bptr, int bsiz, void *op)
  {
  	return pathCompare(aptr, bptr, '/', op);
  }
  
  
  /*
  static int compareFunc(const char *aptr, int asiz, const char *bptr, int bsiz, void *op)
  {
  	return strcmp(aptr, bptr);
  }
  */
  
  /*
  static int compareStrNumFunc(const char *a, int asize, const char *b, int bsize, void *op)
  {
  	long an = atol(a);
  	long bn = atol(b);
  	if (an > bn) return 1;
  	if (an < bn) return -1;
  
  	{
  	int smaller = (asize < bsize) ? asize : bsize;
  	return memcmp(a + smaller + 1, b + smaller + 1, smaller);
  	}
  }
  */
  
  /*
  static int compareStrNumFunc(const char *a, int asize, const char *b, int bsize, void *op)
  {
  	char *as = strchr((char *)a, '/');
  	char *bs = strchr((char *)b, '/');
  	long an;
  	long bn;
  	int r;
  
  	if (as != NULL) *as = 0;
  	if (bs != NULL) *bs = 0;
  
  	an = atol(a);
  	bn = atol(b);
  
  	//printf("%i cmp %i ", an, bn);
  
  	if (as != NULL) *as = '/';
  	if (bs != NULL) *bs = '/';
  
  
  	if (an > bn) { r = 1; }
  	else
  	if (an < bn) { r = -1; }
  	else
  	{
  		// an and bn are the same, so just do a lex compare of the whole
  		r = strcmp(a, b);
  	}
  
  	//printf("%s %s %s\n", a, (r == 0) ? "==" : ((r == 1) ? ">" : "<"), b);
  
  	return r;
  }
  */
  
  IoTag *IoTokyoCabinet_newTag(void *state)
  {
  	IoTag *tag = IoTag_newWithName_(protoId);
  	IoTag_state_(tag, state);
  	IoTag_freeFunc_(tag, (IoTagFreeFunc *)IoTokyoCabinet_free);
  	IoTag_cloneFunc_(tag, (IoTagCloneFunc *)IoTokyoCabinet_rawClone);
  	//IoTag_markFunc_(tag, (IoTagMarkFunc *)IoTokyoCabinet_mark);
  	return tag;
  }
  
  IoTokyoCabinet *IoTokyoCabinet_proto(void *state)
  {
  	IoMethodTable methodTable[] = {
  	{"open",      IoTokyoCabinet_open},
  	{"close",     IoTokyoCabinet_close},
  
  	{"atPut",     IoTokyoCabinet_atPut},
  	{"atAppend",  IoTokyoCabinet_atAppend},
  	{"at",        IoTokyoCabinet_at},
  	{"sizeAt",    IoTokyoCabinet_sizeAt},
  	{"removeAt",  IoTokyoCabinet_removeAt},
  	{"sync",      IoTokyoCabinet_sync},
  
  	{"size",      IoTokyoCabinet_size},
  	//{"optimize",  IoTokyoCabinet_optimize},
  	{"path",      IoTokyoCabinet_path},
  
  	{"begin",  IoTokyoCabinet_begin},
  	{"commit",  IoTokyoCabinet_commit},
  	{"abort",  IoTokyoCabinet_abort},
  
  	{"cursor",  IoTokyoCabinet_cursor},
  	{"prefixCursor",  IoTokyoCabinet_prefixCursor},
  
  	{NULL, NULL},
  	};
  
  	IoObject *self = IoObject_new(state);
  	IoObject_tag_(self, IoTokyoCabinet_newTag(state));
  
  	IoObject_setDataPointer_(self, NULL);
  	IoState_registerProtoWithId_((IoState *)state, self, protoId);
  
  	IoObject_addMethodTable_(self, methodTable);
  	return self;
  }
  
  IoTokyoCabinet *IoTokyoCabinet_rawClone(IoTokyoCabinet *proto)
  {
  	IoObject *self = IoObject_rawClonePrimitive(proto);
  	IoObject_tag_(self, IoObject_tag(proto));
  	IoObject_setDataPointer_(self, tcbdbnew());
  	return self;
  }
  
  IoTokyoCabinet *IoTokyoCabinet_new(void *state)
  {
  	IoObject *proto = IoState_protoWithId_((IoState *)state, protoId);
  	return IOCLONE(proto);
  }
  
  void IoTokyoCabinet_free(IoTokyoCabinet *self)
  {
  	if(TokyoCabinet(self))
  	{
  		tcbdbdel(TokyoCabinet(self));
  		IoObject_setDataPointer_(self, NULL);
  	}
  }
  
  /*
  void IoTokyoCabinet_mark(IoTokyoCabinet *self)
  {
  }
  */
  
  // --------------------------------------------------------
  
  IoObject *IoTokyoCabinet_open(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet open(path)
  	Opens the database.
  	*/
  	
  	BDBCMP cf = pathCompareFunc;
  	IoSeq *path = IoObject_getSlot_(self, IOSYMBOL("path"));
  
  	IOASSERT(ISSEQ(path), "path must be a sequence");
  
  	IoTokyoCabinet_close(self, locals, m);
  	
  	{
  		IoSeq *compareType = IoObject_getSlot_(self, IOSYMBOL("compareType"));
  		//printf("using compareType:%s\n", CSTRING(compareType));
  		IOASSERT(ISSEQ(compareType), "compareType must be a sequence");
  
  		if(strcmp(CSTRING(compareType), "lexical") == 0) { cf = tcbdbcmplexical; } else
  		if(strcmp(CSTRING(compareType), "decimal") == 0) { cf = tcbdbcmpdecimal; } else
  		if(strcmp(CSTRING(compareType), "int32")   == 0) { cf = tcbdbcmpint32;   } else
  		if(strcmp(CSTRING(compareType), "int64")   == 0) { cf = tcbdbcmpint64;   } else
  		if(strcmp(CSTRING(compareType), "path")    == 0) { cf = pathCompareFunc; }
  		else
  		{
  			fprintf(stderr, "ivalid compare function name\n");
  			return IONIL(self);
  		}
  	}
  
  	IoObject_setDataPointer_(self, tcbdbnew());
  	tcbdbsetcmpfunc(TokyoCabinet(self), cf, NULL);
  	
  	if(!tcbdbopen(TokyoCabinet(self), CSTRING(path), BDBOWRITER | BDBOCREAT | BDBOLCKNB))
  	{
  		fprintf(stderr, "tcbdbopen failed\n");
  		return IONIL(self);
  	}
  
  
  
  	return self;
  }
  
  IoObject *IoTokyoCabinet_close(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet close
  	Closes the database.
  	*/
  
  	if(TokyoCabinet(self))
  	{
  		tcbdbclose(TokyoCabinet(self));
  		IoObject_setDataPointer_(self, NULL);
  	}
  
  	return self;
  }
  
  IoObject *IoTokyoCabinet_sync(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet sync
  	Syncs the database. Returns self
  	*/
  	
  	int result;
  	IOASSERT(TokyoCabinet(self), "invalid TokyoCabinet");
  	result = tcbdbsync(TokyoCabinet(self));
  	IOASSERT(result, tcbdberrmsg(tcbdbecode(TokyoCabinet(self))));
  	return self;
  }
  
  IoObject *IoTokyoCabinet_size(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet size
  	Returns number of records in the database. Returns self
  	*/
  	
  	int result;
  	IOASSERT(TokyoCabinet(self), "invalid TokyoCabinet");
  	result = tcbdbrnum(TokyoCabinet(self));
  	return IONUMBER(result);
  }
  
  IoObject *IoTokyoCabinet_optimize(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet optimize
  	Optimizes the database. Returns self
  	*/
  
  	IOASSERT(TokyoCabinet(self), "invalid TokyoCabinet");
  	//IOASSERT(vloptimize(TokyoCabinet(self)), tcbdberrmsg(tcbdbecode(TokyoCabinet(self))));
  	return self;
  }
  
  IoObject *IoTokyoCabinet_path(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet path
  	Returns the path of the database.
  	*/
  
  	IOASSERT(TokyoCabinet(self), "invalid TokyoCabinet");
  	return IOSYMBOL(tcbdbpath(TokyoCabinet(self)));
  }
  
  IoObject *IoTokyoCabinet_begin(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet begin
  	Begin transaction. Returns self
  	*/
  
  	IOASSERT(TokyoCabinet(self), "invalid TokyoCabinet");
  	IOASSERT(tcbdbtranbegin(TokyoCabinet(self)), tcbdberrmsg(tcbdbecode(TokyoCabinet(self))));
  	return self;
  }
  
  IoObject *IoTokyoCabinet_commit(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet commit
  	Commit transaction. Returns self
  	*/
  
  	IOASSERT(TokyoCabinet(self), "invalid TokyoCabinet");
  	IOASSERT(tcbdbtrancommit(TokyoCabinet(self)), tcbdberrmsg(tcbdbecode(TokyoCabinet(self))));
  	return self;
  }
  
  IoObject *IoTokyoCabinet_abort(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet abort
  	Abort transaction. Returns self
  	*/
  	
  	int result;
  	IOASSERT(TokyoCabinet(self), "invalid TokyoCabinet");
  	result = tcbdbtranabort(TokyoCabinet(self));
  	IOASSERT(result, tcbdberrmsg(tcbdbecode(TokyoCabinet(self))));
  	return self;
  }
  
  // ---------------------------------------------------------------------------------
  
  IoObject *IoTokyoCabinet_atPut(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet atPut(keySymbol, valueSequence)
  	Sets the value of valueSequence with the key keySymbol. Returns self.
  	*/
  
  	IoSeq *key = IoMessage_locals_seqArgAt_(m, locals, 0);
  	IoSeq *value = IoMessage_locals_seqArgAt_(m, locals, 1);
  	int result;
  
  	IOASSERT(TokyoCabinet(self), "invalid TokyoCabinet");
  
  	result = tcbdbput(TokyoCabinet(self), 
  		(const void *)IoSeq_rawBytes(key),  (int)IoSeq_rawSizeInBytes(key), 
  		(const void *)IoSeq_rawBytes(value), (int)IoSeq_rawSizeInBytes(value));
  
  	IOASSERT(result, tcbdberrmsg(tcbdbecode(TokyoCabinet(self))));
  
  	return self;
  }
  
  IoObject *IoTokyoCabinet_atAppend(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet atApple(keySymbol, valueSequence)
  	Appends valueSequence to the current value at keySymbol. Returns self.
  	*/
  
  	IoSeq *key = IoMessage_locals_seqArgAt_(m, locals, 0);
  	IoSeq *value = IoMessage_locals_seqArgAt_(m, locals, 1);
  	int result;
  
  	IOASSERT(TokyoCabinet(self), "invalid TokyoCabinet");
  
  	result = tcbdbputcat(TokyoCabinet(self), 
  		(const void *)IoSeq_rawBytes(key),  (int)IoSeq_rawSizeInBytes(key), 
  		(const void *)IoSeq_rawBytes(value), (int)IoSeq_rawSizeInBytes(value));
  
  	IOASSERT(result, tcbdberrmsg(tcbdbecode(TokyoCabinet(self))));
  
  	return self;
  }
  
  IoObject *IoTokyoCabinet_at(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet at(keySymbol)
  	Returns a Sequence for the value at the given key or nil if there is no such key.
  	*/
  	
  	IoSeq *key = IoMessage_locals_seqArgAt_(m, locals, 0);
  	void *value;
  	int size;
  
  	IOASSERT(TokyoCabinet(self), "invalid TokyoCabinet");
  
  	value = tcbdbget(TokyoCabinet(self), (const char *)IoSeq_rawBytes(key), IoSeq_rawSizeInBytes(key), &size);
  
  	if (value)
  	{
  		IoSeq *v = IoSeq_newWithData_length_(IOSTATE, (unsigned char *)value, size);
  		free(value);
  		return v;
  	}
  
  	return IONIL(self);
  }
  
  IoObject *IoTokyoCabinet_sizeAt(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet sizeAt(keySymbol)
  	Returns the size of the value at the given key or nil if there is no such key.
  	*/
  	
  	IoSeq *key = IoMessage_locals_seqArgAt_(m, locals, 0);
  	int size;
  
  	IOASSERT(TokyoCabinet(self), "invalid TokyoCabinet");
  
  	size = tcbdbvsiz(TokyoCabinet(self), (const void *)IoSeq_rawBytes(key), (int)IoSeq_rawSizeInBytes(key));
  
  	if (size == -1)
  	{
  		return IONIL(self);
  	}
  
  	return IONUMBER(size);
  }
  
  IoObject *IoTokyoCabinet_removeAt(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet atRemove(keySymbol)
  	Removes the specified key. Returns self
  	*/
  	
  	IoSeq *key = IoMessage_locals_seqArgAt_(m, locals, 0);
  	int result;
  	IOASSERT(TokyoCabinet(self), "invalid TokyoCabinet");
  	result = tcbdbout(TokyoCabinet(self), (const void *)IoSeq_rawBytes(key), (int)IoSeq_rawSizeInBytes(key));
  	//IOASSERT(result, tcbdberrmsg(tcbdbecode(TokyoCabinet(self)))); // commented to avoid 'no item found' exception
  	return self;
  }
  
  #include "IoTokyoCabinetCursor.h"
  
  IoObject *IoTokyoCabinet_cursor(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet cursor
  	Returns a new cursor object.
  	*/
  	
  	return IoTokyoCabinetCursor_newWithDB_(IOSTATE, TokyoCabinet(self));
  }
  
  #include "IoTokyoCabinetPrefixCursor.h"
  
  IoObject *IoTokyoCabinet_prefixCursor(IoObject *self, IoObject *locals, IoMessage *m)
  {
  	/*doc TokyoCabinet prefixCursor
  	Returns a new prefix cursor object.
  	*/
  	
  	return IoTokyoCabinetPrefixCursor_newWithDB_(IOSTATE, TokyoCabinet(self));
  }
  