diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,2 @@
+Copyright (c) 2014, david feng
+public domain
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/cbits/alloc.c b/cbits/alloc.c
new file mode 100644
--- /dev/null
+++ b/cbits/alloc.c
@@ -0,0 +1,84 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+static int _g = 0;
+
+void * _pbcM_malloc(size_t sz) {
+	++ _g;
+	return malloc(sz);
+}
+
+void _pbcM_free(void *p) {
+	if (p) {
+		-- _g;
+		free(p);
+	}
+}
+
+void* _pbcM_realloc(void *p, size_t sz) {
+	return realloc(p,sz);
+}
+
+void _pbcM_memory() {
+	printf("%d\n",_g);	
+}
+
+struct heap_page {
+	struct heap_page * next;
+};
+
+struct heap {
+	struct heap_page *current;
+	int size;
+	int used;
+};
+
+struct heap * 
+_pbcH_new(int pagesize) {
+	int cap = 1024;
+	while(cap < pagesize) {
+		cap *= 2;
+	}
+	struct heap * h = (struct heap *)_pbcM_malloc(sizeof(struct heap));
+	h->current = (struct heap_page *)_pbcM_malloc(sizeof(struct heap_page) + cap);
+	h->size = cap;
+	h->used = 0;
+	h->current->next = NULL;
+	return h;
+}
+
+void 
+_pbcH_delete(struct heap *h) {
+	struct heap_page * p = h->current;
+	struct heap_page * next = p->next;
+	for(;;) {
+		_pbcM_free(p);
+		if (next == NULL)
+			break;
+		p = next;
+		next = p->next;
+	}
+	_pbcM_free(h);
+}
+
+void* 
+_pbcH_alloc(struct heap *h, int size) {
+	size = (size + 3) & ~3;
+	if (h->size - h->used < size) {
+		struct heap_page * p;
+		if (size < h->size) {
+			p = (struct heap_page *)_pbcM_malloc(sizeof(struct heap_page) + h->size);
+		} else {
+			p = (struct heap_page *)_pbcM_malloc(sizeof(struct heap_page) + size);
+		}
+		p->next = h->current;
+		h->current = p;
+		h->used = size;
+		return (p+1);
+	} else {
+		char * buffer = (char *)(h->current + 1);
+		buffer += h->used;
+		h->used += size;
+		return buffer;
+	}
+}
diff --git a/cbits/alloc.h b/cbits/alloc.h
new file mode 100644
--- /dev/null
+++ b/cbits/alloc.h
@@ -0,0 +1,37 @@
+#ifndef PROTOBUF_C_ALLOC_H
+#define PROTOBUF_C_ALLOC_H
+
+#include <stdlib.h>
+#include <string.h>
+
+void * _pbcM_malloc(size_t sz);
+void _pbcM_free(void *p);
+void * _pbcM_realloc(void *p, size_t sz);
+void _pbcM_memory();
+
+struct heap;
+
+struct heap * _pbcH_new(int pagesize);
+void _pbcH_delete(struct heap *);
+void* _pbcH_alloc(struct heap *, int size);
+
+#define HMALLOC(size) ((h) ? _pbcH_alloc(h, size) : _pbcM_malloc(size))
+
+#define malloc _pbcM_malloc
+#define free _pbcM_free
+#define realloc _pbcM_realloc
+#define memory _pbcM_memory
+
+#ifdef _WIN32
+
+#include <malloc.h>
+
+#endif
+
+#ifdef _MSC_VER
+
+#define alloca _alloca
+
+#endif
+
+#endif
diff --git a/cbits/array.c b/cbits/array.c
new file mode 100644
--- /dev/null
+++ b/cbits/array.c
@@ -0,0 +1,139 @@
+#include "pbc.h"
+#include "array.h"
+#include "alloc.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+struct array {
+	int number;
+	struct heap *heap;
+	union _pbc_var * a;
+};
+
+#define INNER_FIELD ((PBC_ARRAY_CAP - sizeof(struct array)) / sizeof(pbc_var))
+
+void 
+_pbcA_open(pbc_array _array) {
+	struct array * a = (struct array *)_array;
+	a->number = 0;
+	a->heap = NULL;
+	a->a = (union _pbc_var *)(a+1);
+}
+
+void 
+_pbcA_open_heap(pbc_array _array, struct heap *h) {
+	struct array * a = (struct array *)_array;
+	a->number = 0;
+	a->heap = h;
+	a->a = (union _pbc_var *)(a+1);
+}
+
+void 
+_pbcA_close(pbc_array _array) {
+	struct array * a = (struct array *)_array;
+	if (a->heap == NULL && a->a != NULL && (union _pbc_var *)(a+1) != a->a) {
+		_pbcM_free(a->a);
+		a->a = NULL;
+	}
+}
+
+void 
+_pbcA_push(pbc_array _array, pbc_var var) {
+	struct array * a = (struct array *)_array;
+	if (a->number == 0) {
+		a->a = (union _pbc_var *)(a+1);
+	} else if (a->number >= INNER_FIELD) {
+		if (a->number == INNER_FIELD) {
+			int cap = 1;
+			while (cap <= a->number + 1) 
+				cap *= 2;
+			struct heap * h = a->heap;
+			union _pbc_var * outer = (union _pbc_var *)HMALLOC(cap * sizeof(union _pbc_var));
+			memcpy(outer , a->a , INNER_FIELD * sizeof(pbc_var));
+			a->a = outer;
+		} else {
+			int size=a->number;
+			if (((size + 1) ^ size) > size) {
+				struct heap * h = a->heap;
+				if (h) {
+					void * old = a->a;
+					a->a = (union _pbc_var *)_pbcH_alloc(h, sizeof(union _pbc_var) * (size+1) * 2);
+					memcpy(a->a, old, sizeof(union _pbc_var) * size);
+				} else {
+					a->a = (union _pbc_var *)_pbcM_realloc(a->a,sizeof(union _pbc_var) * (size+1) * 2);
+				}
+			}
+		}
+	}
+	a->a[a->number] = *var;
+	++ a->number;
+}
+
+void 
+_pbcA_index(pbc_array _array, int idx, pbc_var var)
+{
+	struct array * a = (struct array *)_array;
+	var[0] = a->a[idx];
+}
+
+void *
+_pbcA_index_p(pbc_array _array, int idx)
+{
+	struct array * a = (struct array *)_array;
+	return &(a->a[idx]);
+}
+
+int 
+pbc_array_size(pbc_array _array) {
+	struct array * a = (struct array *)_array;
+	return a->number;
+}
+
+uint32_t 
+pbc_array_integer(pbc_array array, int index, uint32_t *hi) {
+	pbc_var var;
+	_pbcA_index(array , index , var);
+	if (hi) {
+		*hi = var->integer.hi;
+	}
+	return var->integer.low;
+}
+
+double 
+pbc_array_real(pbc_array array, int index) {
+	pbc_var var;
+	_pbcA_index(array , index , var);
+	return var->real;
+}
+
+struct pbc_slice *
+pbc_array_slice(pbc_array _array, int index) {
+	struct array * a = (struct array *)_array;
+	if (index <0 || index > a->number) {
+		return NULL;
+	}
+	return (struct pbc_slice *) &(a->a[index]);
+}
+
+void 
+pbc_array_push_integer(pbc_array array, uint32_t low, uint32_t hi) {
+	pbc_var var;
+	var->integer.low = low;
+	var->integer.hi = hi;
+	_pbcA_push(array,var);
+}
+
+void 
+pbc_array_push_slice(pbc_array array, struct pbc_slice *s) {
+	pbc_var var;
+	var->m = *s;
+	_pbcA_push(array,var);
+}
+
+void 
+pbc_array_push_real(pbc_array array, double v) {
+	pbc_var var;
+	var->real = v;
+	_pbcA_push(array,var);
+}
diff --git a/cbits/array.h b/cbits/array.h
new file mode 100644
--- /dev/null
+++ b/cbits/array.h
@@ -0,0 +1,31 @@
+#ifndef PROTOBUF_C_ARRAY_H
+#define PROTOBUF_C_ARRAY_H
+
+#include "varint.h"
+#include "pbc.h"
+#include "alloc.h"
+
+typedef union _pbc_var {
+	struct longlong integer;
+	double real;
+	struct {
+		const char * str;
+		int len;
+	} s;
+	struct {
+		int id;
+		const char * name;
+	} e;
+	struct pbc_slice m;
+	void * p[2];
+} pbc_var[1];
+
+void _pbcA_open(pbc_array);
+void _pbcA_open_heap(pbc_array, struct heap *h);
+void _pbcA_close(pbc_array);
+
+void _pbcA_push(pbc_array, pbc_var var);
+void _pbcA_index(pbc_array , int idx, pbc_var var);
+void * _pbcA_index_p(pbc_array _array, int idx);
+
+#endif
diff --git a/cbits/bootstrap.c b/cbits/bootstrap.c
new file mode 100644
--- /dev/null
+++ b/cbits/bootstrap.c
@@ -0,0 +1,303 @@
+#include "pbc.h"
+#include "map.h"
+#include "context.h"
+#include "pattern.h"
+#include "proto.h"
+#include "alloc.h"
+#include "bootstrap.h"
+#include "stringpool.h"
+#include "array.h"
+#include "descriptor.pbc.h"
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdint.h>
+
+/*
+
+// Descriptor
+
+// google.protobuf.Descriptor.proto encoded in descriptor.pbc.h with proto pbc.file .
+
+package pbc;
+
+message field {
+	optional string name = 1;
+	optional int32 id = 2;
+	optional int32 label = 3;	// 0 optional 1 required 2 repeated 
+	optional int32 type = 4;	// type_id
+	optional string type_name = 5;	
+	optional int32 default_int = 6;	 
+	optional string default_string = 7;
+	optional double default_real = 8;
+}
+
+message file {
+	optional string name = 1;
+	repeated string dependency = 2;
+
+	repeated string message_name = 3;
+	repeated int32 message_size = 4;
+	repeated field message_field = 5;
+
+	repeated string enum_name = 6;
+	repeated int32 enum_size = 7;
+	repeated string enum_string = 8;
+	repeated int32 enum_id = 9;
+}
+
+*/
+
+struct field_t {
+	struct pbc_slice name;
+	int32_t id;
+	int32_t label;
+	int32_t type;
+	struct pbc_slice type_name;
+	int32_t default_integer;
+	struct pbc_slice default_string;
+	double default_real;
+};
+
+struct file_t {
+	struct pbc_slice name;	// string
+	pbc_array dependency;	// string
+	pbc_array message_name;	// string
+	pbc_array message_size;	// int32
+	pbc_array message_field;	// field_t
+	pbc_array enum_name;	// string
+	pbc_array enum_size;	// int32
+	pbc_array enum_string;	// string
+	pbc_array enum_id;	// int32
+};
+
+static void
+set_enum_one(struct pbc_env *p, struct file_t *file, const char *name, int start, int sz) {
+	struct map_kv *table = (struct map_kv *)malloc(sz * sizeof(struct map_kv));
+	int i;
+	for (i=0;i<sz;i++) {
+		pbc_var id;
+		pbc_var string;
+		_pbcA_index(file->enum_id, start+i, id);
+		_pbcA_index(file->enum_string, start+i, string);
+		table[i].id = (int)id->integer.low;
+		table[i].pointer = (void *)string->s.str;
+	}
+	_pbcP_push_enum(p,name,table,sz);
+
+	free(table);
+}
+
+static void
+set_enums(struct pbc_env *p, struct file_t *file) {
+	int n = pbc_array_size(file->enum_size);
+	int i;
+	int start = 0;
+	for (i=0;i<n;i++) {
+		pbc_var name;
+		_pbcA_index(file->enum_name,i,name);
+		pbc_var var;
+		_pbcA_index(file->enum_size,i,var);
+		set_enum_one(p, file, name->s.str, start , (int)var->integer.low);
+		start += var->integer.low;
+	}
+}
+
+static void
+set_default(struct _field *f, struct field_t *input) {
+	switch (f->type) {
+	case PTYPE_DOUBLE:
+	case PTYPE_FLOAT:
+		f->default_v->real = input->default_real;
+		break;
+	case PTYPE_STRING:
+	case PTYPE_ENUM:
+		f->default_v->m = input->default_string;
+		break;
+	default:
+		f->default_v->integer.low = input->default_integer;
+		break;
+	}
+}
+
+static void
+set_msg_one(struct pbc_pattern * FIELD_T, struct pbc_env *p, struct file_t *file, const char *name, int start, int sz , pbc_array queue) {
+	int i;
+	for (i=0;i<sz;i++) {
+		pbc_var _field;
+		_pbcA_index(file->message_field, start+i, _field);
+		struct field_t field;
+
+		int ret = pbc_pattern_unpack(FIELD_T, &_field->m, &field);
+		if (ret != 0) {
+			continue;
+		}
+		struct _field f;
+		f.id = field.id;
+		f.name = (const char *)field.name.buffer;
+		f.type = field.type;
+		f.label = field.label;
+		f.type_name.n = (const char *)field.type_name.buffer;
+		set_default(&f, &field);
+
+		_pbcP_push_message(p,name, &f , queue);
+
+		// don't need to close pattern since no array
+	}
+	_pbcP_init_message(p, name);
+}
+
+static void
+set_msgs(struct pbc_pattern * FIELD_T, struct pbc_env *p, struct file_t *file , pbc_array queue) {
+	int n = pbc_array_size(file->message_size);
+	int i;
+	int start = 0;
+	for (i=0;i<n;i++) {
+		pbc_var name;
+		_pbcA_index(file->message_name,i,name);
+		pbc_var sz;
+		_pbcA_index(file->message_size,i,sz);
+		set_msg_one(FIELD_T, p, file, name->s.str, start , (int)sz->integer.low , queue);
+		start += sz->integer.low;
+	}
+}
+
+static void
+set_field_one(struct pbc_env *p, struct _field *f) {
+	const char * type_name = f->type_name.n;
+	if (f->type == PTYPE_MESSAGE) {
+		f->type_name.m  = (struct _message *)_pbcM_sp_query(p->msgs, type_name);
+//		printf("MESSAGE: %s %p\n",type_name, f->type_name.m);
+	} else if (f->type == PTYPE_ENUM) {
+		f->type_name.e = (struct _enum *)_pbcM_sp_query(p->enums, type_name);
+//		printf("ENUM: %s %p ",type_name, f->type_name.e);
+		const char * str = f->default_v->s.str;
+		if (str && str[0]) {
+			int err = _pbcM_si_query(f->type_name.e->name, str , &(f->default_v->e.id));
+			if (err < 0)
+				goto _default;
+			f->default_v->e.name = (const char *)_pbcM_ip_query(f->type_name.e->id, f->default_v->e.id);
+//			printf("[%s %d]\n",str,f->default_v->e.id);
+		} else {
+_default:
+			memcpy(f->default_v, f->type_name.e->default_v, sizeof(pbc_var));
+//			printf("(%s %d)\n",f->default_v->e.name,f->default_v->e.id);
+		}
+	}
+}
+
+void
+_pbcB_register_fields(struct pbc_env *p, pbc_array queue) {
+	int sz = pbc_array_size(queue);
+	int i;
+	for (i=0;i<sz;i++) {
+		pbc_var atom;
+		_pbcA_index(queue,i,atom);
+		struct _field * f = (struct _field *)atom->m.buffer;
+		set_field_one(p, f);
+	}
+}
+
+static void
+_set_string(struct _pattern_field * f) {
+	f->ptype = PTYPE_STRING;
+	f->ctype = CTYPE_VAR;
+	f->defv->s.str = "";
+	f->defv->s.len = 0;
+}
+
+static void
+_set_int32(struct _pattern_field * f) {
+	f->ptype = PTYPE_INT32;
+	f->ctype = CTYPE_INT32;
+}
+
+static void
+_set_double(struct _pattern_field * f) {
+	f->ptype = PTYPE_DOUBLE;
+	f->ctype = CTYPE_DOUBLE;
+}
+
+static void
+_set_message_array(struct _pattern_field *f) {
+	f->ptype = PTYPE_MESSAGE;
+	f->ctype = CTYPE_ARRAY;
+}
+
+static void
+_set_string_array(struct _pattern_field * f) {
+	f->ptype = PTYPE_STRING;
+	f->ctype = CTYPE_ARRAY;
+}
+
+static void
+_set_int32_array(struct _pattern_field * f) {
+	f->ptype = PTYPE_INT32;
+	f->ctype = CTYPE_ARRAY;
+}
+
+#define SET_PATTERN(pat , idx , pat_type, field_name , type) \
+	pat->f[idx].id = idx+1 ; \
+	pat->f[idx].offset = offsetof(struct pat_type, field_name);	\
+	_set_##type(&pat->f[idx]);
+
+#define F(idx,field_name,type) SET_PATTERN(FIELD_T, idx, field_t ,field_name, type)
+#define D(idx,field_name,type) SET_PATTERN(FILE_T, idx, file_t ,field_name, type)
+
+static int
+register_internal(struct pbc_env * p, struct pbc_slice *slice) {
+	struct pbc_pattern * FIELD_T =  _pbcP_new(p,8);
+	F(0,name,string);
+	F(1,id,int32);
+	F(2,label,int32);
+	F(3,type,int32);
+	F(4,type_name,string);
+	F(5,default_integer,int32);
+	F(6,default_string,string);
+	F(7,default_real,double);
+
+	struct pbc_pattern * FILE_T =  _pbcP_new(p,10);
+
+	D(0,name,string);
+	D(1,dependency,string_array);
+	D(2,message_name,string_array);
+	D(3,message_size,int32_array);
+	D(4,message_field,message_array);
+	D(5,enum_name,string_array);
+	D(6,enum_size,int32_array);
+	D(7,enum_string,string_array);
+	D(8,enum_id,int32_array);
+
+	int ret = 0;
+
+	struct file_t file;
+	int r = pbc_pattern_unpack(FILE_T, slice, &file);
+	if (r != 0) {
+		ret = 1;
+		goto _return;
+	}
+
+	_pbcM_sp_insert(p->files , (const char *)file.name.buffer, NULL);
+
+	pbc_array queue;
+	_pbcA_open(queue);
+
+	set_enums(p, &file);
+	set_msgs(FIELD_T, p, &file, queue);
+	_pbcB_register_fields(p, queue);
+
+	_pbcA_close(queue);
+	pbc_pattern_close_arrays(FILE_T, &file);
+
+_return:
+	free(FIELD_T);
+	free(FILE_T);
+	return ret;
+}
+
+void 
+_pbcB_init(struct pbc_env * p) {
+	struct pbc_slice slice = { pbc_descriptor,sizeof(pbc_descriptor) };
+	register_internal(p,&slice);
+}
diff --git a/cbits/bootstrap.h b/cbits/bootstrap.h
new file mode 100644
--- /dev/null
+++ b/cbits/bootstrap.h
@@ -0,0 +1,10 @@
+#ifndef PROTOBUF_C_BOOTSTRAP_H
+#define PROTOBUF_C_BOOTSTRAP_H
+
+#include "proto.h"
+#include "pbc.h"
+
+void _pbcB_init(struct pbc_env *);
+void _pbcB_register_fields(struct pbc_env *, pbc_array queue);
+
+#endif
diff --git a/cbits/context.c b/cbits/context.c
new file mode 100644
--- /dev/null
+++ b/cbits/context.c
@@ -0,0 +1,280 @@
+#include "pbc.h"
+#include "alloc.h"
+#include "varint.h"
+#include "context.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#ifndef _MSC_VER
+#include <stdbool.h>
+#endif
+
+#define INNER_ATOM ((PBC_CONTEXT_CAP - sizeof(struct context)) / sizeof(struct atom))
+
+static char * 
+wiretype_decode(uint8_t *buffer, int cap , struct atom *a , int start)
+{
+	uint8_t temp[10];
+	struct longlong r;
+	int len;
+	if (cap >= 10) {
+		len = _pbcV_decode(buffer, &r);
+		if (r.hi !=0)
+			return NULL;
+	} else {
+		memcpy(temp, buffer , cap);
+		len = _pbcV_decode(temp, &r);
+		if (len > cap || r.hi !=0)
+			return NULL;
+	}
+
+	int wiretype = r.low & 7;
+	a->wire_id = r.low;
+	buffer += len;
+	start += len;
+	cap -=len;
+	
+	switch (wiretype) {
+	case WT_VARINT :
+		if (cap >=10) {
+			len = _pbcV_decode(buffer, &a->v.i);
+		} else {
+			memcpy(temp, buffer , cap);
+			len = _pbcV_decode(temp, &a->v.i);
+			if (cap < len)
+				return NULL;
+		}
+		return (char *)buffer+len;
+	case WT_BIT64 :
+		if (cap < 8)
+			return NULL;
+		a->v.i.low = buffer[0] |
+			buffer[1] << 8 |
+			buffer[2] << 16 |
+			buffer[3] << 24;
+		a->v.i.hi = buffer[4] |
+			buffer[5] << 8 |
+			buffer[6] << 16 |
+			buffer[7] << 24;
+		return (char *)buffer + 8;
+	case WT_LEND :
+		if (cap >=10) {
+			len = _pbcV_decode(buffer, &r);
+		} else {
+			memcpy(temp, buffer , cap);
+			len = _pbcV_decode(temp, &r);
+		}
+		if (cap < len + r.low || r.hi !=0)
+			return NULL;
+		a->v.s.start = start + len;
+		a->v.s.end = start + len + r.low;
+		return (char *)buffer + len + r.low;
+	case WT_BIT32 :
+		if (cap < 4)
+			return NULL;
+		a->v.i.low = buffer[0] |
+			buffer[1] << 8 |
+			buffer[2] << 16 |
+			buffer[3] << 24;
+		a->v.i.hi = 0;
+		return (char *)buffer + 4;
+	default:
+		return NULL;
+	}
+}
+
+static inline int
+_decode_varint(uint8_t * buffer, int size , struct atom * a) {
+	a->wire_id = WT_VARINT;
+	if (size < 10) {
+		uint8_t temp[10];
+		memcpy(temp,buffer,size);
+		return _pbcV_decode(temp , &(a->v.i));
+	} else {
+		return _pbcV_decode(buffer , &(a->v.i));
+	}
+}
+
+static int
+_open_packed_varint(struct context * ctx , uint8_t * buffer, int size) {
+	struct atom * a = (struct atom *)(ctx + 1);
+
+	int i;
+
+	for (i=0;i<INNER_ATOM;i++) {
+		if (size == 0)
+			break;
+		int len = _decode_varint(buffer, size, &a[i]);
+		buffer += len;
+		size -= len;
+	}
+
+	if (size == 0) {
+		ctx->a = a;
+	} else {
+		int cap = 64;
+		ctx->a = (struct atom *)malloc(cap * sizeof(struct atom));
+		while (size > 0) {
+			if (i >= cap) {
+				cap = cap + 64;
+				ctx->a = (struct atom *)realloc(ctx->a, cap * sizeof(struct atom));
+				continue;
+			}
+			int len = _decode_varint(buffer, size, &a[i]);
+			buffer += len;
+			size -= len;
+
+			++i;
+		}
+		memcpy(ctx->a, a , sizeof(struct atom) * INNER_ATOM);
+	}
+	ctx->number = i;
+
+	return i;
+}
+
+int 
+_pbcC_open_packed(pbc_ctx _ctx, int ptype, void *buffer, int size) {
+	struct context * ctx = (struct context *)_ctx;
+	ctx->buffer = (char *)buffer;
+	ctx->size = size;
+	ctx->number = 0;
+	ctx->a = NULL;
+
+	if (buffer == NULL || size == 0) {
+		return 0;
+	}
+
+	int bits = 0;
+
+	switch (ptype) {
+	case PTYPE_INT64:
+	case PTYPE_UINT64:
+	case PTYPE_INT32:
+	case PTYPE_BOOL:
+	case PTYPE_UINT32:
+	case PTYPE_ENUM:
+	case PTYPE_SINT32:
+	case PTYPE_SINT64:
+		return _open_packed_varint(ctx , (uint8_t *)buffer, size);
+	case PTYPE_DOUBLE:
+	case PTYPE_FIXED64:
+	case PTYPE_SFIXED64:
+		ctx->number = size / 8;
+		bits = 64;
+		break;
+	case PTYPE_FLOAT:
+	case PTYPE_FIXED32:
+	case PTYPE_SFIXED32:
+		ctx->number = size / 4;
+		bits = 32;
+		break;
+	default:
+		return 0;
+	}
+
+	struct atom * a = (struct atom *)(ctx + 1);
+
+	if (ctx->number > INNER_ATOM) {
+		ctx->a = (struct atom *)malloc(ctx->number * sizeof(struct atom));
+		a = ctx->a;
+	} else {
+		ctx->a = a;
+	}
+
+	int i;
+	if (bits == 64) {
+		uint8_t * data = (uint8_t *)buffer;
+		for (i=0;i<ctx->number;i++) {
+			a[i].wire_id = WT_BIT64;
+			a[i].v.i.low = data[0] |
+				data[1] << 8 |
+				data[2] << 16 |
+				data[3] << 24;
+			a[i].v.i.hi = data[4] |
+				data[5] << 8 |
+				data[6] << 16 |
+				data[7] << 24;
+			data += 8;
+		}
+	} else {
+		uint8_t * data = (uint8_t *)buffer;
+		for (i=0;i<ctx->number;i++) {
+			a[i].wire_id = WT_BIT32;
+			a[i].v.i.low = data[0] |
+				data[1] << 8 |
+				data[2] << 16 |
+				data[3] << 24;
+			a[i].v.i.hi = 0;
+			data += 4;
+		}
+	}
+
+	return ctx->number;
+}
+
+int 
+_pbcC_open(pbc_ctx _ctx , void *buffer, int size) {
+	struct context * ctx = (struct context *)_ctx;
+	ctx->buffer = (char *)buffer;
+	ctx->size = size;
+
+	if (buffer == NULL || size == 0) {
+		ctx->number = 0;
+		ctx->a = NULL;
+		return 0;
+	}
+
+	struct atom * a = (struct atom *)(ctx + 1);
+
+	int i;
+	int start = 0;
+
+	ctx->a = a;
+
+	for (i=0;i<INNER_ATOM;i++) {
+		if (size == 0)
+			break;
+		char * next = wiretype_decode((uint8_t *)buffer, size , &a[i] , start);
+		if (next == NULL)
+			return -i;
+		start += next - (char *)buffer;
+		size -= next - (char *)buffer;
+		buffer = next;
+	}
+
+	if (size > 0) {
+		int cap = 64;
+		ctx->a = (struct atom *)malloc(cap * sizeof(struct atom));
+		while (size > 0) {
+			if (i >= cap) {
+				cap = cap + 64;
+				ctx->a = (struct atom *)realloc(ctx->a, cap * sizeof(struct atom));
+				continue;
+			}
+			char * next = wiretype_decode((uint8_t *)buffer, size , &ctx->a[i] , start);
+			if (next == NULL) {
+				return -i;
+			}
+			start += next - (char *)buffer;
+			size -= next - (char *)buffer;
+			buffer = next;
+			++i;
+		}
+		memcpy(ctx->a, a , sizeof(struct atom) * INNER_ATOM);
+	}
+	ctx->number = i;
+
+	return i;
+}
+
+
+void 
+_pbcC_close(pbc_ctx _ctx) {
+	struct context * ctx = (struct context *)_ctx;
+	if (ctx->a != NULL && (struct atom *)(ctx+1) != ctx->a) {
+		free(ctx->a);
+		ctx->a = NULL;
+	}
+}
diff --git a/cbits/context.h b/cbits/context.h
new file mode 100644
--- /dev/null
+++ b/cbits/context.h
@@ -0,0 +1,140 @@
+#ifndef PROTOBUF_C_CONTEXT_H
+#define PROTOBUF_C_CONTEXT_H
+
+#include <stdint.h>
+
+#include "array.h"
+
+#define PBC_CONTEXT_CAP 256
+
+// wiretype
+
+#define WT_VARINT 0
+#define WT_BIT64 1
+#define WT_LEND 2
+#define WT_BIT32 5
+
+#define CTYPE_INT32 1
+#define CTYPE_INT64 2
+#define CTYPE_DOUBLE 3
+#define CTYPE_FLOAT 4
+#define CTYPE_POINTER 5
+#define CTYPE_BOOL 6
+#define CTYPE_INT8 7
+#define CTYPE_INT16 8
+#define CTYPE_ARRAY 9
+#define CTYPE_VAR 10
+#define CTYPE_PACKED 11
+
+#define PTYPE_DOUBLE   1
+#define PTYPE_FLOAT    2
+#define PTYPE_INT64    3   // Not ZigZag encoded.  Negative numbers take 10 bytes.  Use TYPE_SINT64 if negative values are likely.
+#define PTYPE_UINT64   4
+#define PTYPE_INT32    5   // Not ZigZag encoded.  Negative numbers take 10 bytes.  Use TYPE_SINT32 if negative values are likely.
+#define PTYPE_FIXED64  6
+#define PTYPE_FIXED32  7
+#define PTYPE_BOOL     8
+#define PTYPE_STRING   9
+#define PTYPE_GROUP    10  // Tag-delimited aggregate.
+#define PTYPE_MESSAGE  11  // Length-delimited aggregate.
+#define PTYPE_BYTES    12
+#define PTYPE_UINT32   13
+#define PTYPE_ENUM     14
+#define PTYPE_SFIXED32 15
+#define PTYPE_SFIXED64 16
+#define PTYPE_SINT32   17  // Uses ZigZag encoding.
+#define PTYPE_SINT64   18  // Uses ZigZag encoding.
+
+struct slice {
+	int start;
+	int end;
+};
+
+struct atom {
+	int wire_id;
+	union {
+		struct slice s;
+		struct longlong i;
+	} v;
+};
+
+struct context {
+	char * buffer;
+	int size;
+	int number;
+	struct atom * a;
+};
+
+typedef struct _pbc_ctx { char _data[PBC_CONTEXT_CAP]; } pbc_ctx[1];
+
+int _pbcC_open(pbc_ctx , void *buffer, int size);	// <=0 failed
+int _pbcC_open_packed(pbc_ctx _ctx, int ptype, void *buffer, int size);
+void _pbcC_close(pbc_ctx);
+
+static inline double
+read_double(struct atom * a) {
+	union {
+		uint64_t i;
+		double d;
+	} u;
+	u.i = (uint64_t) a->v.i.low | (uint64_t) a->v.i.hi << 32;
+	return u.d;
+}
+
+static inline float
+read_float(struct atom * a) {
+	union {
+		uint32_t i;
+		float f;
+	} u;
+	u.i = a->v.i.low;
+	return u.f;
+}
+
+static inline void
+double_encode(double v , uint8_t * buffer) {
+	union {
+		double v;
+		uint64_t e;
+	} u;
+	u.v = v;
+	buffer[0] = (uint8_t) (u.e & 0xff);
+	buffer[1] = (uint8_t) (u.e >> 8 & 0xff);
+	buffer[2] = (uint8_t) (u.e >> 16 & 0xff);
+	buffer[3] = (uint8_t) (u.e >> 24 & 0xff);
+	buffer[4] = (uint8_t) (u.e >> 32 & 0xff);
+	buffer[5] = (uint8_t) (u.e >> 40 & 0xff);
+	buffer[6] = (uint8_t) (u.e >> 48 & 0xff);
+	buffer[7] = (uint8_t) (u.e >> 56 & 0xff);
+}
+
+static inline void
+float_encode(float v , uint8_t * buffer) {
+	union {
+		float v;
+		uint32_t e;
+	} u;
+	u.v = v;
+	buffer[0] = (uint8_t) (u.e & 0xff);
+	buffer[1] = (uint8_t) (u.e >> 8 & 0xff);
+	buffer[2] = (uint8_t) (u.e >> 16 & 0xff);
+	buffer[3] = (uint8_t) (u.e >> 24 & 0xff);
+}
+
+#define CHECK_LEND(a,err) if ((a->wire_id & 7) != WT_LEND) return err;
+
+#if 0
+/* maybe we don't need check these wire type */
+#define CHECK_VARINT(a,err) if ((a->wire_id & 7) != WT_VARINT) return err;
+#define CHECK_BIT32(a,err) if ((a->wire_id & 7) != WT_BIT32) return err;
+#define CHECK_BIT64(a,err) if ((a->wire_id & 7) != WT_BIT64) return err;
+
+#else
+
+#define CHECK_VARINT(a,err)
+#define CHECK_BIT32(a,err)
+#define CHECK_BIT64(a,err)
+
+#endif
+
+#endif
diff --git a/cbits/decode.c b/cbits/decode.c
new file mode 100644
--- /dev/null
+++ b/cbits/decode.c
@@ -0,0 +1,349 @@
+#include "pbc.h"
+#include "alloc.h"
+#include "context.h"
+#include "proto.h"
+#include "varint.h"
+
+#include <assert.h>
+
+static const char * TYPENAME[] = {
+	"invalid",	// 0
+	"integer",	// 1
+	"real",	// 2
+	"boolean",	// 3
+	"enum",	// 4
+	"string",	// 5
+	"message",	// 6
+	"fixed64",	// 7
+	"fixed32",	// 8
+	"bytes",	// 9
+	"int64",	// 10
+	"uint",	// 11
+};
+
+static int
+call_unknown(pbc_decoder f, void * ud, int id, struct atom *a, uint8_t * start) {
+	union pbc_value v;
+	switch (a->wire_id & 7) {
+	case WT_VARINT:
+		v.i.low = a->v.i.low;
+		v.i.hi = a->v.i.hi;
+		f(ud, PBC_INT, TYPENAME[PBC_INT], &v, id , NULL);
+		break;
+	case WT_BIT64:
+		v.i.low = a->v.i.low;
+		v.i.hi = a->v.i.hi;
+		f(ud, PBC_FIXED64, TYPENAME[PBC_FIXED64], &v, id , NULL);
+		break;
+	case WT_LEND:
+		v.s.buffer = (char*)start + a->v.s.start;
+		v.s.len = a->v.s.end - a->v.s.start;
+		f(ud, PBC_BYTES, TYPENAME[PBC_BYTES], &v, id , NULL);
+		break;
+	case WT_BIT32:
+		v.i.low = a->v.i.low;
+		v.i.hi = 0;
+		f(ud, PBC_FIXED32, TYPENAME[PBC_FIXED32], &v, id , NULL);
+		break;
+	default:
+		return 1;
+	}
+	return 0;
+}
+
+static int
+call_type(pbc_decoder pd, void * ud, struct _field *f, struct atom *a, uint8_t * start) {
+	union pbc_value v;
+	const char * type_name = NULL;
+	int type = _pbcP_type(f, &type_name);
+	assert(type != 0);
+	if (type_name == NULL) {
+		type_name = TYPENAME[type & ~PBC_REPEATED];
+	}
+	switch (f->type) {
+	case PTYPE_DOUBLE:
+		CHECK_BIT64(a, -1);
+		v.f = read_double(a);
+		break;
+	case PTYPE_FLOAT:
+		CHECK_BIT32(a, -1);
+		v.f = (double) read_float(a);
+		break;
+	case PTYPE_ENUM:
+		CHECK_VARINT(a, -1);
+		v.e.id = a->v.i.low;
+		v.e.name = (const char *)_pbcM_ip_query(f->type_name.e->id , v.e.id);
+		break;
+	case PTYPE_INT64:
+	case PTYPE_UINT64:
+		CHECK_VARINT(a, -1);
+		v.i.low = a->v.i.low;
+		v.i.hi = a->v.i.hi;
+		break;
+	case PTYPE_FIXED64:
+	case PTYPE_SFIXED64:
+		CHECK_BIT64(a, -1);
+		v.i.low = a->v.i.low;
+		v.i.hi = a->v.i.hi;
+		break;
+	case PTYPE_INT32:
+	case PTYPE_UINT32:
+	case PTYPE_BOOL:
+		CHECK_VARINT(a, -1);
+		v.i.low = a->v.i.low;
+		v.i.hi = 0;
+		break;
+	case PTYPE_FIXED32:
+	case PTYPE_SFIXED32:
+		CHECK_BIT32(a, -1);
+		v.i.low = a->v.i.low;
+		v.i.hi = 0;
+		break;
+	case PTYPE_SINT32: 
+		CHECK_VARINT(a, -1);
+		v.i.low = a->v.i.low;
+		v.i.hi = a->v.i.hi;
+		_pbcV_dezigzag32((struct longlong *)&(v.i));
+		break;
+	case PTYPE_SINT64:
+		CHECK_VARINT(a, -1);
+		v.i.low = a->v.i.low;
+		v.i.hi = a->v.i.hi;
+		_pbcV_dezigzag64((struct longlong *)&(v.i));
+		break;
+	case PTYPE_STRING:
+	case PTYPE_BYTES:
+	case PTYPE_MESSAGE:
+		CHECK_LEND(a, -1);
+		v.s.buffer = start + a->v.s.start;
+		v.s.len = a->v.s.end - a->v.s.start;
+		break;
+	default:
+		assert(0);
+		break;
+	}
+	pd(ud, type, type_name, &v, f->id, f->name);
+	return 0;
+}
+
+static int
+call_array(pbc_decoder pd, void * ud, struct _field *f, uint8_t * buffer , int size) {
+	union pbc_value v;
+	const char * type_name = NULL;
+	int type = _pbcP_type(f, &type_name);
+	assert(type != 0);
+	if (type_name == NULL) {
+		type_name = TYPENAME[type & ~PBC_REPEATED];
+	}
+	v.i.hi = 0;
+	int i;
+	switch(f->type) {
+		case PTYPE_DOUBLE:
+			if (size % 8 != 0) {
+				return -1;
+			}
+			for (i=0;i<size;i+=8) {
+				union {
+					double d;
+					uint64_t i64;
+				} u;
+				u.i64 = (uint64_t)buffer[i] |
+					(uint64_t)buffer[i+1] << 8 |
+					(uint64_t)buffer[i+2] << 16 |
+					(uint64_t)buffer[i+3] << 24 |
+					(uint64_t)buffer[i+4] << 32 |
+					(uint64_t)buffer[i+5] << 40 |
+					(uint64_t)buffer[i+6] << 48 |
+					(uint64_t)buffer[i+7] << 56;
+				v.f = u.d;
+				pd(ud, type , type_name, &v, f->id, f->name);
+			}
+			return size/8;
+		case PTYPE_FLOAT:
+			if (size % 4 != 0)
+				return -1;
+			for (i=0;i<size;i+=4) {
+				union {
+					float f;
+					uint32_t i32;
+				} u;
+				u.i32 = (uint32_t)buffer[i] |
+					(uint32_t)buffer[i+1] << 8 |
+					(uint32_t)buffer[i+2] << 16 |
+					(uint32_t)buffer[i+3] << 24;
+				v.f = (double)u.f;
+				pd(ud, type , type_name, &v, f->id, f->name);
+			}
+			return size/4;
+		case PTYPE_FIXED32:
+		case PTYPE_SFIXED32:
+			if (size % 4 != 0)
+				return -1;
+			for (i=0;i<size;i+=4) {
+				v.i.low = (uint32_t)buffer[i] |
+					(uint32_t)buffer[i+1] << 8 |
+					(uint32_t)buffer[i+2] << 16 |
+					(uint32_t)buffer[i+3] << 24;
+				pd(ud, type , type_name, &v, f->id, f->name);
+			}
+			return size/4;
+		case PTYPE_FIXED64:
+		case PTYPE_SFIXED64:
+			if (size % 8 != 0)
+				return -1;
+			for (i=0;i<size;i+=8) {
+				v.i.low = (uint32_t)buffer[i] |
+					(uint32_t)buffer[i+1] << 8 |
+					(uint32_t)buffer[i+2] << 16 |
+					(uint32_t)buffer[i+3] << 24;
+				v.i.hi = (uint32_t)buffer[i+4] |
+					(uint32_t)buffer[i+5] << 8 |
+					(uint32_t)buffer[i+6] << 16 |
+					(uint32_t)buffer[i+7] << 24;
+				pd(ud, type , type_name, &v, f->id, f->name);
+			}
+			return size/8;
+		case PTYPE_INT64:
+		case PTYPE_UINT64:
+		case PTYPE_INT32:
+		case PTYPE_UINT32:
+		case PTYPE_BOOL: {
+			int n = 0;
+			while (size > 0) {
+				int len;
+				if (size >= 10) {
+					len = _pbcV_decode(buffer, (struct longlong *)&(v.i));
+				} else {
+					uint8_t temp[10];
+					memcpy(temp, buffer, size);
+					len = _pbcV_decode(buffer, (struct longlong *)&(v.i));
+					if (len > size)
+						return -1;
+				}
+				pd(ud, type , type_name, &v, f->id, f->name);
+				buffer += len;
+				size -= len;
+				++n;
+			}
+			return n;
+		}
+		case PTYPE_ENUM: {
+			int n = 0;
+			while (size > 0) {
+				int len;
+				if (size >= 10) {
+					len = _pbcV_decode(buffer, (struct longlong *)&(v.i));
+				} else {
+					uint8_t temp[10];
+					memcpy(temp, buffer, size);
+					len = _pbcV_decode(buffer, (struct longlong *)&(v.i));
+					if (len > size)
+						return -1;
+				}
+				v.e.id = v.i.low;
+				v.e.name = (const char *)_pbcM_ip_query(f->type_name.e->id , v.i.low);
+				pd(ud, type , type_name, &v, f->id, f->name);
+				buffer += len;
+				size -= len;
+				++n;
+			}
+			return n;
+		}
+		case PTYPE_SINT32: {
+			int n = 0;
+			while (size > 0) {
+				int len;
+				if (size >= 10) {
+					len = _pbcV_decode(buffer, (struct longlong *)&(v.i));
+					_pbcV_dezigzag32((struct longlong *)&(v.i));
+				} else {
+					uint8_t temp[10];
+					memcpy(temp, buffer, size);
+					len = _pbcV_decode(buffer, (struct longlong *)&(v.i));
+					if (len > size)
+						return -1;
+					_pbcV_dezigzag32((struct longlong *)&(v.i));
+				}
+				pd(ud, type , type_name, &v, f->id, f->name);
+				buffer += len;
+				size -= len;
+				++n;
+			}
+			return n;
+		}
+		case PTYPE_SINT64: {
+			int n = 0;
+			while (size > 0) {
+				int len;
+				if (size >= 10) {
+					len = _pbcV_decode(buffer, (struct longlong *)&(v.i));
+					_pbcV_dezigzag64((struct longlong *)&(v.i));
+				} else {
+					uint8_t temp[10];
+					memcpy(temp, buffer, size);
+					len = _pbcV_decode(buffer, (struct longlong *)&(v.i));
+					if (len > size)
+						return -1;
+					_pbcV_dezigzag64((struct longlong *)&(v.i));
+				}
+				pd(ud, type , type_name, &v, f->id, f->name);
+				buffer += len;
+				size -= len;
+				++n;
+			}
+			return n;
+		}
+		default:
+			return -1;
+	}
+}
+
+int
+pbc_decode(struct pbc_env * env, const char * type_name , struct pbc_slice * slice, pbc_decoder pd, void *ud) {
+	struct _message * msg = _pbcP_get_message(env, type_name);
+	if (msg == NULL) {
+		env->lasterror = "Proto not found";
+		return -1;
+	}
+	if (slice->len == 0) {
+		return 0;
+	}
+	pbc_ctx _ctx;
+	int count = _pbcC_open(_ctx,slice->buffer,slice->len);
+	if (count <= 0) {
+		env->lasterror = "decode context error";
+		_pbcC_close(_ctx);
+		return count - 1;
+	}
+	struct context * ctx = (struct context *)_ctx;
+	uint8_t * start = (uint8_t *)slice->buffer;
+
+	int i;
+	for (i=0;i<ctx->number;i++) {
+		int id = ctx->a[i].wire_id >> 3;
+		struct _field * f = (struct _field *)_pbcM_ip_query(msg->id , id);
+		if (f==NULL) {
+			int err = call_unknown(pd,ud,id,&ctx->a[i],start);
+			if (err) {
+				_pbcC_close(_ctx);
+				return -i-1;
+			}
+		} else if (f->label == LABEL_PACKED) {
+			struct atom * a = &ctx->a[i];
+			int n = call_array(pd, ud, f , start + a->v.s.start , a->v.s.end - a->v.s.start);
+			if (n < 0) {
+				_pbcC_close(_ctx);
+				return -i-1;
+			}
+		} else {
+			if (call_type(pd,ud,f,&ctx->a[i],start) != 0) {
+				_pbcC_close(_ctx);
+				return -i-1;
+			}
+		}
+	}
+
+	_pbcC_close(_ctx);
+	return ctx->number;
+}
+
diff --git a/cbits/descriptor.pbc.h b/cbits/descriptor.pbc.h
new file mode 100644
--- /dev/null
+++ b/cbits/descriptor.pbc.h
@@ -0,0 +1,271 @@
+static unsigned char pbc_descriptor[] = {
+72,1,72,2,72,3,72,4,72,5,72,6,72,7,72,8,
+72,9,72,10,72,11,72,12,72,13,72,14,72,15,72,16,
+72,17,72,18,72,1,72,2,72,3,72,1,72,2,72,3,
+72,0,72,1,72,2,50,42,103,111,111,103,108,101,46,112,
+114,111,116,111,98,117,102,46,70,105,101,108,100,68,101,115,
+99,114,105,112,116,111,114,80,114,111,116,111,46,84,121,112,
+101,0,50,43,103,111,111,103,108,101,46,112,114,111,116,111,
+98,117,102,46,70,105,101,108,100,68,101,115,99,114,105,112,
+116,111,114,80,114,111,116,111,46,76,97,98,101,108,0,50,
+41,103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,
+46,70,105,108,101,79,112,116,105,111,110,115,46,79,112,116,
+105,109,105,122,101,77,111,100,101,0,50,35,103,111,111,103,
+108,101,46,112,114,111,116,111,98,117,102,46,70,105,101,108,
+100,79,112,116,105,111,110,115,46,67,84,121,112,101,0,66,
+12,84,89,80,69,95,68,79,85,66,76,69,0,66,11,84,
+89,80,69,95,70,76,79,65,84,0,66,11,84,89,80,69,
+95,73,78,84,54,52,0,66,12,84,89,80,69,95,85,73,
+78,84,54,52,0,66,11,84,89,80,69,95,73,78,84,51,
+50,0,66,13,84,89,80,69,95,70,73,88,69,68,54,52,
+0,66,13,84,89,80,69,95,70,73,88,69,68,51,50,0,
+66,10,84,89,80,69,95,66,79,79,76,0,66,12,84,89,
+80,69,95,83,84,82,73,78,71,0,66,11,84,89,80,69,
+95,71,82,79,85,80,0,66,13,84,89,80,69,95,77,69,
+83,83,65,71,69,0,66,11,84,89,80,69,95,66,89,84,
+69,83,0,66,12,84,89,80,69,95,85,73,78,84,51,50,
+0,66,10,84,89,80,69,95,69,78,85,77,0,66,14,84,
+89,80,69,95,83,70,73,88,69,68,51,50,0,66,14,84,
+89,80,69,95,83,70,73,88,69,68,54,52,0,66,12,84,
+89,80,69,95,83,73,78,84,51,50,0,66,12,84,89,80,
+69,95,83,73,78,84,54,52,0,66,15,76,65,66,69,76,
+95,79,80,84,73,79,78,65,76,0,66,15,76,65,66,69,
+76,95,82,69,81,85,73,82,69,68,0,66,15,76,65,66,
+69,76,95,82,69,80,69,65,84,69,68,0,66,6,83,80,
+69,69,68,0,66,10,67,79,68,69,95,83,73,90,69,0,
+66,13,76,73,84,69,95,82,85,78,84,73,77,69,0,66,
+7,83,84,82,73,78,71,0,66,5,67,79,82,68,0,66,
+13,83,84,82,73,78,71,95,80,73,69,67,69,0,56,18,
+56,3,56,3,56,3,10,11,100,101,115,99,114,105,112,116,
+111,114,0,32,1,32,9,32,7,32,2,32,8,32,3,32,
+3,32,3,32,4,32,9,32,3,32,5,32,1,32,1,32,
+1,32,1,32,7,32,2,32,1,32,2,42,51,24,2,16,
+1,32,11,42,36,103,111,111,103,108,101,46,112,114,111,116,
+111,98,117,102,46,70,105,108,101,68,101,115,99,114,105,112,
+116,111,114,80,114,111,116,111,0,10,5,102,105,108,101,0,
+42,13,32,9,24,0,10,5,110,97,109,101,0,16,1,42,
+16,32,9,24,0,10,8,112,97,99,107,97,103,101,0,16,
+2,42,19,32,9,24,2,10,11,100,101,112,101,110,100,101,
+110,99,121,0,16,3,42,55,24,2,16,4,32,11,42,32,
+103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,46,
+68,101,115,99,114,105,112,116,111,114,80,114,111,116,111,0,
+10,13,109,101,115,115,97,103,101,95,116,121,112,101,0,42,
+56,24,2,16,5,32,11,42,36,103,111,111,103,108,101,46,
+112,114,111,116,111,98,117,102,46,69,110,117,109,68,101,115,
+99,114,105,112,116,111,114,80,114,111,116,111,0,10,10,101,
+110,117,109,95,116,121,112,101,0,42,57,24,2,16,6,32,
+11,42,39,103,111,111,103,108,101,46,112,114,111,116,111,98,
+117,102,46,83,101,114,118,105,99,101,68,101,115,99,114,105,
+112,116,111,114,80,114,111,116,111,0,10,8,115,101,114,118,
+105,99,101,0,42,57,24,2,16,7,32,11,42,37,103,111,
+111,103,108,101,46,112,114,111,116,111,98,117,102,46,70,105,
+101,108,100,68,101,115,99,114,105,112,116,111,114,80,114,111,
+116,111,0,10,10,101,120,116,101,110,115,105,111,110,0,42,
+46,24,0,16,8,32,11,42,28,103,111,111,103,108,101,46,
+112,114,111,116,111,98,117,102,46,70,105,108,101,79,112,116,
+105,111,110,115,0,10,8,111,112,116,105,111,110,115,0,42,
+58,24,0,16,9,32,11,42,31,103,111,111,103,108,101,46,
+112,114,111,116,111,98,117,102,46,83,111,117,114,99,101,67,
+111,100,101,73,110,102,111,0,10,17,115,111,117,114,99,101,
+95,99,111,100,101,95,105,110,102,111,0,42,13,32,9,24,
+0,10,5,110,97,109,101,0,16,1,42,53,24,2,16,2,
+32,11,42,37,103,111,111,103,108,101,46,112,114,111,116,111,
+98,117,102,46,70,105,101,108,100,68,101,115,99,114,105,112,
+116,111,114,80,114,111,116,111,0,10,6,102,105,101,108,100,
+0,42,57,24,2,16,6,32,11,42,37,103,111,111,103,108,
+101,46,112,114,111,116,111,98,117,102,46,70,105,101,108,100,
+68,101,115,99,114,105,112,116,111,114,80,114,111,116,111,0,
+10,10,101,120,116,101,110,115,105,111,110,0,42,54,24,2,
+16,3,32,11,42,32,103,111,111,103,108,101,46,112,114,111,
+116,111,98,117,102,46,68,101,115,99,114,105,112,116,111,114,
+80,114,111,116,111,0,10,12,110,101,115,116,101,100,95,116,
+121,112,101,0,42,56,24,2,16,4,32,11,42,36,103,111,
+111,103,108,101,46,112,114,111,116,111,98,117,102,46,69,110,
+117,109,68,101,115,99,114,105,112,116,111,114,80,114,111,116,
+111,0,10,10,101,110,117,109,95,116,121,112,101,0,42,73,
+24,2,16,5,32,11,42,47,103,111,111,103,108,101,46,112,
+114,111,116,111,98,117,102,46,68,101,115,99,114,105,112,116,
+111,114,80,114,111,116,111,46,69,120,116,101,110,115,105,111,
+110,82,97,110,103,101,0,10,16,101,120,116,101,110,115,105,
+111,110,95,114,97,110,103,101,0,42,49,24,0,16,7,32,
+11,42,31,103,111,111,103,108,101,46,112,114,111,116,111,98,
+117,102,46,77,101,115,115,97,103,101,79,112,116,105,111,110,
+115,0,10,8,111,112,116,105,111,110,115,0,42,14,32,5,
+24,0,10,6,115,116,97,114,116,0,16,1,42,12,32,5,
+24,0,10,4,101,110,100,0,16,2,42,13,32,9,24,0,
+10,5,110,97,109,101,0,16,1,42,15,32,5,24,0,10,
+7,110,117,109,98,101,114,0,16,3,42,59,24,0,16,4,
+32,14,42,43,103,111,111,103,108,101,46,112,114,111,116,111,
+98,117,102,46,70,105,101,108,100,68,101,115,99,114,105,112,
+116,111,114,80,114,111,116,111,46,76,97,98,101,108,0,10,
+6,108,97,98,101,108,0,42,57,24,0,16,5,32,14,42,
+42,103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,
+46,70,105,101,108,100,68,101,115,99,114,105,112,116,111,114,
+80,114,111,116,111,46,84,121,112,101,0,10,5,116,121,112,
+101,0,42,18,32,9,24,0,10,10,116,121,112,101,95,110,
+97,109,101,0,16,6,42,17,32,9,24,0,10,9,101,120,
+116,101,110,100,101,101,0,16,2,42,22,32,9,24,0,10,
+14,100,101,102,97,117,108,116,95,118,97,108,117,101,0,16,
+7,42,47,24,0,16,8,32,11,42,29,103,111,111,103,108,
+101,46,112,114,111,116,111,98,117,102,46,70,105,101,108,100,
+79,112,116,105,111,110,115,0,10,8,111,112,116,105,111,110,
+115,0,42,13,32,9,24,0,10,5,110,97,109,101,0,16,
+1,42,57,24,2,16,2,32,11,42,41,103,111,111,103,108,
+101,46,112,114,111,116,111,98,117,102,46,69,110,117,109,86,
+97,108,117,101,68,101,115,99,114,105,112,116,111,114,80,114,
+111,116,111,0,10,6,118,97,108,117,101,0,42,46,24,0,
+16,3,32,11,42,28,103,111,111,103,108,101,46,112,114,111,
+116,111,98,117,102,46,69,110,117,109,79,112,116,105,111,110,
+115,0,10,8,111,112,116,105,111,110,115,0,42,13,32,9,
+24,0,10,5,110,97,109,101,0,16,1,42,15,32,5,24,
+0,10,7,110,117,109,98,101,114,0,16,2,42,51,24,0,
+16,3,32,11,42,33,103,111,111,103,108,101,46,112,114,111,
+116,111,98,117,102,46,69,110,117,109,86,97,108,117,101,79,
+112,116,105,111,110,115,0,10,8,111,112,116,105,111,110,115,
+0,42,13,32,9,24,0,10,5,110,97,109,101,0,16,1,
+42,55,24,2,16,2,32,11,42,38,103,111,111,103,108,101,
+46,112,114,111,116,111,98,117,102,46,77,101,116,104,111,100,
+68,101,115,99,114,105,112,116,111,114,80,114,111,116,111,0,
+10,7,109,101,116,104,111,100,0,42,49,24,0,16,3,32,
+11,42,31,103,111,111,103,108,101,46,112,114,111,116,111,98,
+117,102,46,83,101,114,118,105,99,101,79,112,116,105,111,110,
+115,0,10,8,111,112,116,105,111,110,115,0,42,13,32,9,
+24,0,10,5,110,97,109,101,0,16,1,42,19,32,9,24,
+0,10,11,105,110,112,117,116,95,116,121,112,101,0,16,2,
+42,20,32,9,24,0,10,12,111,117,116,112,117,116,95,116,
+121,112,101,0,16,3,42,48,24,0,16,4,32,11,42,30,
+103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,46,
+77,101,116,104,111,100,79,112,116,105,111,110,115,0,10,8,
+111,112,116,105,111,110,115,0,42,21,32,9,24,0,10,13,
+106,97,118,97,95,112,97,99,107,97,103,101,0,16,1,42,
+29,32,9,24,0,10,21,106,97,118,97,95,111,117,116,101,
+114,95,99,108,97,115,115,110,97,109,101,0,16,8,42,30,
+24,0,16,10,32,8,10,20,106,97,118,97,95,109,117,108,
+116,105,112,108,101,95,102,105,108,101,115,0,48,0,42,40,
+24,0,16,20,32,8,10,30,106,97,118,97,95,103,101,110,
+101,114,97,116,101,95,101,113,117,97,108,115,95,97,110,100,
+95,104,97,115,104,0,48,0,42,72,24,0,16,9,32,14,
+42,41,103,111,111,103,108,101,46,112,114,111,116,111,98,117,
+102,46,70,105,108,101,79,112,116,105,111,110,115,46,79,112,
+116,105,109,105,122,101,77,111,100,101,0,10,13,111,112,116,
+105,109,105,122,101,95,102,111,114,0,58,6,83,80,69,69,
+68,0,42,30,24,0,16,16,32,8,10,20,99,99,95,103,
+101,110,101,114,105,99,95,115,101,114,118,105,99,101,115,0,
+48,0,42,32,24,0,16,17,32,8,10,22,106,97,118,97,
+95,103,101,110,101,114,105,99,95,115,101,114,118,105,99,101,
+115,0,48,0,42,30,24,0,16,18,32,8,10,20,112,121,
+95,103,101,110,101,114,105,99,95,115,101,114,118,105,99,101,
+115,0,48,0,42,68,24,2,16,231,7,32,11,42,36,103,
+111,111,103,108,101,46,112,114,111,116,111,98,117,102,46,85,
+110,105,110,116,101,114,112,114,101,116,101,100,79,112,116,105,
+111,110,0,10,21,117,110,105,110,116,101,114,112,114,101,116,
+101,100,95,111,112,116,105,111,110,0,42,34,24,0,16,1,
+32,8,10,24,109,101,115,115,97,103,101,95,115,101,116,95,
+119,105,114,101,95,102,111,114,109,97,116,0,48,0,42,42,
+24,0,16,2,32,8,10,32,110,111,95,115,116,97,110,100,
+97,114,100,95,100,101,115,99,114,105,112,116,111,114,95,97,
+99,99,101,115,115,111,114,0,48,0,42,68,24,2,16,231,
+7,32,11,42,36,103,111,111,103,108,101,46,112,114,111,116,
+111,98,117,102,46,85,110,105,110,116,101,114,112,114,101,116,
+101,100,79,112,116,105,111,110,0,10,21,117,110,105,110,116,
+101,114,112,114,101,116,101,100,95,111,112,116,105,111,110,0,
+42,60,24,0,16,1,32,14,42,35,103,111,111,103,108,101,
+46,112,114,111,116,111,98,117,102,46,70,105,101,108,100,79,
+112,116,105,111,110,115,46,67,84,121,112,101,0,10,6,99,
+116,121,112,101,0,58,7,83,84,82,73,78,71,0,42,15,
+32,8,24,0,10,7,112,97,99,107,101,100,0,16,2,42,
+21,24,0,16,3,32,8,10,11,100,101,112,114,101,99,97,
+116,101,100,0,48,0,42,29,32,9,24,0,10,21,101,120,
+112,101,114,105,109,101,110,116,97,108,95,109,97,112,95,107,
+101,121,0,16,9,42,68,24,2,16,231,7,32,11,42,36,
+103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,46,
+85,110,105,110,116,101,114,112,114,101,116,101,100,79,112,116,
+105,111,110,0,10,21,117,110,105,110,116,101,114,112,114,101,
+116,101,100,95,111,112,116,105,111,110,0,42,68,24,2,16,
+231,7,32,11,42,36,103,111,111,103,108,101,46,112,114,111,
+116,111,98,117,102,46,85,110,105,110,116,101,114,112,114,101,
+116,101,100,79,112,116,105,111,110,0,10,21,117,110,105,110,
+116,101,114,112,114,101,116,101,100,95,111,112,116,105,111,110,
+0,42,68,24,2,16,231,7,32,11,42,36,103,111,111,103,
+108,101,46,112,114,111,116,111,98,117,102,46,85,110,105,110,
+116,101,114,112,114,101,116,101,100,79,112,116,105,111,110,0,
+10,21,117,110,105,110,116,101,114,112,114,101,116,101,100,95,
+111,112,116,105,111,110,0,42,68,24,2,16,231,7,32,11,
+42,36,103,111,111,103,108,101,46,112,114,111,116,111,98,117,
+102,46,85,110,105,110,116,101,114,112,114,101,116,101,100,79,
+112,116,105,111,110,0,10,21,117,110,105,110,116,101,114,112,
+114,101,116,101,100,95,111,112,116,105,111,110,0,42,68,24,
+2,16,231,7,32,11,42,36,103,111,111,103,108,101,46,112,
+114,111,116,111,98,117,102,46,85,110,105,110,116,101,114,112,
+114,101,116,101,100,79,112,116,105,111,110,0,10,21,117,110,
+105,110,116,101,114,112,114,101,116,101,100,95,111,112,116,105,
+111,110,0,42,60,24,2,16,2,32,11,42,45,103,111,111,
+103,108,101,46,112,114,111,116,111,98,117,102,46,85,110,105,
+110,116,101,114,112,114,101,116,101,100,79,112,116,105,111,110,
+46,78,97,109,101,80,97,114,116,0,10,5,110,97,109,101,
+0,42,25,32,9,24,0,10,17,105,100,101,110,116,105,102,
+105,101,114,95,118,97,108,117,101,0,16,3,42,27,32,4,
+24,0,10,19,112,111,115,105,116,105,118,101,95,105,110,116,
+95,118,97,108,117,101,0,16,4,42,27,32,3,24,0,10,
+19,110,101,103,97,116,105,118,101,95,105,110,116,95,118,97,
+108,117,101,0,16,5,42,21,32,1,24,0,10,13,100,111,
+117,98,108,101,95,118,97,108,117,101,0,16,6,42,21,32,
+12,24,0,10,13,115,116,114,105,110,103,95,118,97,108,117,
+101,0,16,7,42,24,32,9,24,0,10,16,97,103,103,114,
+101,103,97,116,101,95,118,97,108,117,101,0,16,8,42,18,
+32,9,24,1,10,10,110,97,109,101,95,112,97,114,116,0,
+16,1,42,21,32,8,24,1,10,13,105,115,95,101,120,116,
+101,110,115,105,111,110,0,16,2,42,59,24,2,16,1,32,
+11,42,40,103,111,111,103,108,101,46,112,114,111,116,111,98,
+117,102,46,83,111,117,114,99,101,67,111,100,101,73,110,102,
+111,46,76,111,99,97,116,105,111,110,0,10,9,108,111,99,
+97,116,105,111,110,0,42,13,32,5,24,2,10,5,112,97,
+116,104,0,16,1,42,13,32,5,24,2,10,5,115,112,97,
+110,0,16,2,26,34,103,111,111,103,108,101,46,112,114,111,
+116,111,98,117,102,46,70,105,108,101,68,101,115,99,114,105,
+112,116,111,114,83,101,116,0,26,36,103,111,111,103,108,101,
+46,112,114,111,116,111,98,117,102,46,70,105,108,101,68,101,
+115,99,114,105,112,116,111,114,80,114,111,116,111,0,26,32,
+103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,46,
+68,101,115,99,114,105,112,116,111,114,80,114,111,116,111,0,
+26,47,103,111,111,103,108,101,46,112,114,111,116,111,98,117,
+102,46,68,101,115,99,114,105,112,116,111,114,80,114,111,116,
+111,46,69,120,116,101,110,115,105,111,110,82,97,110,103,101,
+0,26,37,103,111,111,103,108,101,46,112,114,111,116,111,98,
+117,102,46,70,105,101,108,100,68,101,115,99,114,105,112,116,
+111,114,80,114,111,116,111,0,26,36,103,111,111,103,108,101,
+46,112,114,111,116,111,98,117,102,46,69,110,117,109,68,101,
+115,99,114,105,112,116,111,114,80,114,111,116,111,0,26,41,
+103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,46,
+69,110,117,109,86,97,108,117,101,68,101,115,99,114,105,112,
+116,111,114,80,114,111,116,111,0,26,39,103,111,111,103,108,
+101,46,112,114,111,116,111,98,117,102,46,83,101,114,118,105,
+99,101,68,101,115,99,114,105,112,116,111,114,80,114,111,116,
+111,0,26,38,103,111,111,103,108,101,46,112,114,111,116,111,
+98,117,102,46,77,101,116,104,111,100,68,101,115,99,114,105,
+112,116,111,114,80,114,111,116,111,0,26,28,103,111,111,103,
+108,101,46,112,114,111,116,111,98,117,102,46,70,105,108,101,
+79,112,116,105,111,110,115,0,26,31,103,111,111,103,108,101,
+46,112,114,111,116,111,98,117,102,46,77,101,115,115,97,103,
+101,79,112,116,105,111,110,115,0,26,29,103,111,111,103,108,
+101,46,112,114,111,116,111,98,117,102,46,70,105,101,108,100,
+79,112,116,105,111,110,115,0,26,28,103,111,111,103,108,101,
+46,112,114,111,116,111,98,117,102,46,69,110,117,109,79,112,
+116,105,111,110,115,0,26,33,103,111,111,103,108,101,46,112,
+114,111,116,111,98,117,102,46,69,110,117,109,86,97,108,117,
+101,79,112,116,105,111,110,115,0,26,31,103,111,111,103,108,
+101,46,112,114,111,116,111,98,117,102,46,83,101,114,118,105,
+99,101,79,112,116,105,111,110,115,0,26,30,103,111,111,103,
+108,101,46,112,114,111,116,111,98,117,102,46,77,101,116,104,
+111,100,79,112,116,105,111,110,115,0,26,36,103,111,111,103,
+108,101,46,112,114,111,116,111,98,117,102,46,85,110,105,110,
+116,101,114,112,114,101,116,101,100,79,112,116,105,111,110,0,
+26,45,103,111,111,103,108,101,46,112,114,111,116,111,98,117,
+102,46,85,110,105,110,116,101,114,112,114,101,116,101,100,79,
+112,116,105,111,110,46,78,97,109,101,80,97,114,116,0,26,
+31,103,111,111,103,108,101,46,112,114,111,116,111,98,117,102,
+46,83,111,117,114,99,101,67,111,100,101,73,110,102,111,0,
+26,40,103,111,111,103,108,101,46,112,114,111,116,111,98,117,
+102,46,83,111,117,114,99,101,67,111,100,101,73,110,102,111,
+46,76,111,99,97,116,105,111,110,0,
+};
diff --git a/cbits/map.c b/cbits/map.c
new file mode 100644
--- /dev/null
+++ b/cbits/map.c
@@ -0,0 +1,477 @@
+#include "map.h"
+#include "alloc.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+struct _pbcM_ip_slot {
+	int id;
+	void * pointer;
+	int next;
+};
+
+struct map_ip {
+	size_t array_size;
+	void ** array;
+	size_t hash_size;
+	struct _pbcM_ip_slot * slot;
+};
+
+struct _pbcM_si_slot {
+	const char *key;
+	size_t hash;
+	int id;
+	int next;
+};
+
+struct map_si {
+	size_t size;
+	struct _pbcM_si_slot slot[1];
+};
+
+static size_t
+calc_hash(const char *name)
+{
+	size_t len = strlen(name);
+	size_t h = len;
+	size_t step = (len>>5)+1;
+	size_t i;
+	for (i=len; i>=step; i-=step)
+	    h = h ^ ((h<<5)+(h>>2)+(size_t)name[i-1]);
+	return h;
+}
+
+struct map_si *
+_pbcM_si_new(struct map_kv * table, int size)
+{
+	size_t sz = sizeof(struct map_si) + (size-1) * sizeof(struct _pbcM_si_slot);
+	struct map_si * ret = (struct map_si *)malloc(sz);
+	memset(ret,0,sz);
+
+	ret->size = (size_t)size;
+
+	int empty = 0;
+	int i;
+
+	for (i=0;i<size;i++) {
+		size_t hash_full = calc_hash((const char *)table[i].pointer);
+		size_t hash = hash_full % size;
+		struct _pbcM_si_slot * slot = &ret->slot[hash];
+		if (slot->key == NULL) {
+			slot->key = (const char *)table[i].pointer;
+			slot->id = table[i].id;
+			slot->hash = hash_full;
+		} else {
+			while(ret->slot[empty].key != NULL) {
+				++empty;
+			}
+			struct _pbcM_si_slot * empty_slot = &ret->slot[empty];
+			empty_slot->next = slot->next;
+			slot->next = empty + 1;
+			empty_slot->id = table[i].id;
+			empty_slot->key = (const char *)table[i].pointer;
+			empty_slot->hash = hash_full;
+		}
+	}
+
+	return ret;
+}
+
+void 
+_pbcM_si_delete(struct map_si *map)
+{
+	free(map);
+}
+
+int
+_pbcM_si_query(struct map_si *map, const char *key, int *result) 
+{
+	size_t hash_full = calc_hash(key);
+	size_t hash = hash_full % map->size;
+
+	struct _pbcM_si_slot * slot = &map->slot[hash];
+	for (;;) {
+		if (slot->hash == hash_full && strcmp(slot->key, key) == 0) {
+			*result = slot->id;
+			return 0;
+		}
+		if (slot->next == 0) {
+			return 1;
+		}
+		slot = &map->slot[slot->next-1];
+	}
+}
+
+static struct map_ip *
+_pbcM_ip_new_hash(struct map_kv * table, int size)
+{
+	struct map_ip * ret = (struct map_ip *)malloc(sizeof(struct map_ip));
+	ret->array = NULL;
+	ret->array_size = 0;
+	ret->hash_size = (size_t)size;
+	ret->slot = (struct _pbcM_ip_slot *)malloc(sizeof(struct _pbcM_ip_slot) * size);
+	memset(ret->slot,0,sizeof(struct _pbcM_ip_slot) * size);
+	int empty = 0;
+	int i;
+	for (i=0;i<size;i++) {
+		int hash = ((unsigned)table[i].id) % size;
+		struct _pbcM_ip_slot * slot = &ret->slot[hash];
+		if (slot->pointer == NULL) {
+			slot->pointer = table[i].pointer;
+			slot->id = table[i].id;
+		} else {
+			while(ret->slot[empty].pointer != NULL) {
+				++empty;
+			}
+			struct _pbcM_ip_slot * empty_slot = &ret->slot[empty];
+			empty_slot->next = slot->next;
+			slot->next = empty + 1;
+			empty_slot->id = table[i].id;
+			empty_slot->pointer = table[i].pointer;
+		}
+	}
+	return ret;
+}
+
+struct map_ip *
+_pbcM_ip_new(struct map_kv * table, int size)
+{
+	int i;
+	int max = table[0].id;
+	if (max > size * 2 || max < 0)
+		return _pbcM_ip_new_hash(table,size);
+	for (i=1;i<size;i++) {
+		if (table[i].id < 0) {
+			return _pbcM_ip_new_hash(table,size);
+		}
+		if (table[i].id > max) {
+			max = table[i].id;
+			if (max > size * 2)
+				return _pbcM_ip_new_hash(table,size);
+		}
+	}
+	struct map_ip * ret = (struct map_ip *)malloc(sizeof(struct map_ip));
+	ret->hash_size = size;
+	ret->slot = NULL;
+	ret->array_size = max + 1;
+	ret->array = (void **)malloc((max+1) * sizeof(void *));
+	memset(ret->array,0,(max+1) * sizeof(void *));
+	for (i=0;i<size;i++) {
+		ret->array[table[i].id] = table[i].pointer;
+	}
+	return ret;
+}
+
+void
+_pbcM_ip_delete(struct map_ip * map)
+{
+	if (map) {
+		free(map->array);
+		free(map->slot);
+		free(map);
+	}
+}
+
+static void
+_inject(struct map_kv * table, struct map_ip *map)
+{
+	if (map->array) {
+		int n = 0;
+		int i;
+		for (i=0;i<(int)map->array_size;i++) {
+			if (map->array[i]) {
+				table[n].id = i;
+				table[n].pointer = map->array[i];
+				++ n;
+			}
+		}
+	} else {
+		int i;
+		for (i=0;i<(int)map->hash_size;i++) {
+			table[i].id = map->slot[i].id;
+			table[i].pointer = map->slot[i].pointer;
+		}
+	}
+}
+
+struct map_ip *
+_pbcM_ip_combine(struct map_ip *a, struct map_ip *b)
+{
+	int sz = (int)(a->hash_size + b->hash_size);
+	struct map_kv * table = (struct map_kv *)malloc(sz * sizeof(struct map_kv));
+	memset(table , 0 , 	sz * sizeof(struct map_kv));
+	_inject(table, a);
+	_inject(table + a->hash_size, b);
+	struct map_ip * r = _pbcM_ip_new(table, sz);
+	free(table);
+	return r;
+}
+
+void *
+_pbcM_ip_query(struct map_ip * map, int id)
+{
+	if (map == NULL)
+		return NULL;
+	if (map->array) {
+		if (id>=0 && id<(int)map->array_size)
+			return map->array[id];
+		return NULL;
+	}
+	int hash = (unsigned)id % map->hash_size;
+	struct _pbcM_ip_slot * slot = &map->slot[hash];
+	for (;;) {
+		if (slot->id == id) {
+			return slot->pointer;
+		}
+		if (slot->next == 0) {
+			return NULL;
+		}
+		slot = &map->slot[slot->next-1];
+	}
+}
+
+struct _pbcM_sp_slot {
+	const char *key;
+	size_t hash;
+	void *pointer;
+	int next;
+};
+
+struct map_sp {
+	size_t cap;
+	size_t size;
+	struct heap *heap;
+	struct _pbcM_sp_slot * slot;
+};
+
+struct map_sp *
+_pbcM_sp_new(int max , struct heap *h)
+{
+	struct map_sp * ret = (struct map_sp *)HMALLOC(sizeof(struct map_sp));
+	int cap = 1;
+	while (cap < max) {
+		cap *=2;
+	}
+	ret->cap = cap;
+	ret->size = 0;
+	ret->slot = (struct _pbcM_sp_slot *)HMALLOC(ret->cap * sizeof(struct _pbcM_sp_slot));
+	memset(ret->slot,0,sizeof(struct _pbcM_sp_slot) * ret->cap);
+	ret->heap = h;
+	return ret;
+}
+
+void
+_pbcM_sp_delete(struct map_sp *map)
+{
+	if (map && map->heap == NULL) {
+		_pbcM_free(map->slot);
+		_pbcM_free(map);
+	}
+}
+
+static void _pbcM_sp_rehash(struct map_sp *map);
+
+static void
+_pbcM_sp_insert_hash(struct map_sp *map, const char *key, size_t hash_full, void * value)
+{
+	if (map->cap > map->size) {
+		size_t hash = hash_full & (map->cap-1);
+		struct _pbcM_sp_slot * slot = &map->slot[hash];
+		if (slot->key == NULL) {
+			slot->key = key;
+			slot->pointer = value;
+			slot->hash = hash_full;
+		} else {
+			int empty = (hash + 1) & (map->cap-1);
+			while(map->slot[empty].key != NULL) {
+				empty = (empty + 1) & (map->cap-1);
+			}
+			struct _pbcM_sp_slot * empty_slot = &map->slot[empty];
+			empty_slot->next = slot->next;
+			slot->next = empty + 1;
+			empty_slot->pointer = value;
+			empty_slot->key = key;
+			empty_slot->hash = hash_full;
+		}
+		map->size++;
+		return;
+	}
+	_pbcM_sp_rehash(map);
+	_pbcM_sp_insert_hash(map, key, hash_full, value);
+}
+
+static void
+_pbcM_sp_rehash(struct map_sp *map) {
+	struct heap * h = map->heap;
+	struct _pbcM_sp_slot * old_slot = map->slot;
+	size_t size = map->size;
+	map->size = 0;
+	map->cap *= 2;
+	map->slot = (struct _pbcM_sp_slot *)HMALLOC(sizeof(struct _pbcM_sp_slot)*map->cap);
+	memset(map->slot,0,sizeof(struct _pbcM_sp_slot)*map->cap);
+	size_t i;
+	for (i=0;i<size;i++) {
+		_pbcM_sp_insert_hash(map, old_slot[i].key, old_slot[i].hash, old_slot[i].pointer);
+	}
+	if (h == NULL) {
+		_pbcM_free(old_slot);
+	}
+}
+
+static void **
+_pbcM_sp_query_insert_hash(struct map_sp *map, const char *key, size_t hash_full)
+{
+	size_t hash = hash_full & (map->cap-1);
+	struct _pbcM_sp_slot * slot = &map->slot[hash];
+	if (slot->key == NULL) {
+		if (map->cap <= map->size) 
+			goto _rehash;
+		slot->key = key;
+		slot->hash = hash_full;
+		map->size++;
+		return &(slot->pointer);
+	} else {
+		for (;;) {
+			if (slot->hash == hash_full && strcmp(slot->key, key) == 0) 
+				return &(slot->pointer);
+			if (slot->next == 0) {
+				break;
+			}
+			slot = &map->slot[slot->next-1];
+		}
+		if (map->cap <= map->size) 
+			goto _rehash;
+
+		int empty = (hash + 1) & (map->cap-1);
+		while(map->slot[empty].key != NULL) {
+			empty = (empty + 1) & (map->cap-1);
+		}
+		struct _pbcM_sp_slot * empty_slot = &map->slot[empty];
+		empty_slot->next = slot->next;
+		slot->next = empty + 1;
+		empty_slot->key = key;
+		empty_slot->hash = hash_full;
+
+		map->size++;
+
+		return &(empty_slot->pointer);
+	}
+_rehash:
+	_pbcM_sp_rehash(map);
+	return _pbcM_sp_query_insert_hash(map, key, hash_full);
+}
+
+void
+_pbcM_sp_insert(struct map_sp *map, const char *key, void * value)
+{
+	_pbcM_sp_insert_hash(map,key,calc_hash(key),value);
+}
+
+void **
+_pbcM_sp_query_insert(struct map_sp *map, const char *key)
+{
+	return _pbcM_sp_query_insert_hash(map,key,calc_hash(key));
+}
+
+void *
+_pbcM_sp_query(struct map_sp *map, const char *key)
+{
+	if (map == NULL)
+		return NULL;
+	size_t hash_full = calc_hash(key);
+	size_t hash = hash_full & (map->cap -1);
+
+	struct _pbcM_sp_slot * slot = &map->slot[hash];
+	for (;;) {
+		if (slot->hash == hash_full && strcmp(slot->key, key) == 0) {
+			return slot->pointer;
+		}
+		if (slot->next == 0) {
+			return NULL;
+		}
+		slot = &map->slot[slot->next-1];
+	}
+}
+
+void 
+_pbcM_sp_foreach(struct map_sp *map, void (*func)(void *p))
+{
+	size_t i;
+	for (i=0;i<map->cap;i++) {
+		if (map->slot[i].pointer) {
+			func(map->slot[i].pointer);
+		}
+	}
+}
+
+void 
+_pbcM_sp_foreach_ud(struct map_sp *map, void (*func)(void *p, void *ud), void *ud)
+{
+	size_t i;
+	for (i=0;i<map->cap;i++) {
+		if (map->slot[i].pointer) {
+			func(map->slot[i].pointer,ud);
+		}
+	}
+}
+
+static int
+_find_first(struct map_sp *map)
+{
+	size_t i;
+	for (i=0;i<map->cap;i++) {
+		if (map->slot[i].pointer) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+static int
+_find_next(struct map_sp *map, const char *key)
+{
+	size_t hash_full = calc_hash(key);
+	size_t hash = hash_full & (map->cap -1);
+
+	struct _pbcM_sp_slot * slot = &map->slot[hash];
+	for (;;) {
+		if (slot->hash == hash_full && strcmp(slot->key, key) == 0) {
+			int i = slot - map->slot + 1;
+			while(i<map->cap) {
+				if (map->slot[i].pointer) {
+					return i;
+				}
+				++i;
+			}
+			return -1;
+		}
+		if (slot->next == 0) {
+			return -1;
+		}
+		slot = &map->slot[slot->next-1];
+	}
+}
+
+void * 
+_pbcM_sp_next(struct map_sp *map, const char ** key)
+{
+	if (map == NULL) {
+		*key = NULL;
+		return NULL;
+	}
+	int idx;
+	if (*key == NULL) {
+		idx = _find_first(map);
+	} else {
+		idx = _find_next(map, *key);
+	}
+	if (idx < 0) {
+		*key = NULL;
+		return NULL;
+	}
+	*key = map->slot[idx].key;
+	return map->slot[idx].pointer;
+}
+
+
+
diff --git a/cbits/map.h b/cbits/map.h
new file mode 100644
--- /dev/null
+++ b/cbits/map.h
@@ -0,0 +1,33 @@
+#ifndef PROTOBUF_C_MAP_H
+#define PROTOBUF_C_MAP_H
+
+#include "alloc.h"
+
+struct map_ip;
+struct map_si;
+struct map_sp;
+
+struct map_kv {
+	int id;
+	void *pointer;
+};
+
+struct map_si * _pbcM_si_new(struct map_kv * table, int size);
+int _pbcM_si_query(struct map_si *map, const char *key, int *result);
+void _pbcM_si_delete(struct map_si *map);
+
+struct map_ip * _pbcM_ip_new(struct map_kv * table, int size);
+struct map_ip * _pbcM_ip_combine(struct map_ip * a, struct map_ip * b);
+void * _pbcM_ip_query(struct map_ip * map, int id);
+void _pbcM_ip_delete(struct map_ip *map);
+
+struct map_sp * _pbcM_sp_new(int max, struct heap *h);
+void _pbcM_sp_insert(struct map_sp *map, const char *key, void * value);
+void * _pbcM_sp_query(struct map_sp *map, const char *key);
+void ** _pbcM_sp_query_insert(struct map_sp *map, const char *key);
+void _pbcM_sp_delete(struct map_sp *map);
+void _pbcM_sp_foreach(struct map_sp *map, void (*func)(void *p));
+void _pbcM_sp_foreach_ud(struct map_sp *map, void (*func)(void *p, void *ud), void *ud);
+void * _pbcM_sp_next(struct map_sp *map, const char ** key);
+
+#endif
diff --git a/cbits/pattern.c b/cbits/pattern.c
new file mode 100644
--- /dev/null
+++ b/cbits/pattern.c
@@ -0,0 +1,1130 @@
+#include "alloc.h"
+#include "context.h"
+#include "varint.h"
+#include "pattern.h"
+#include "array.h"
+#include "proto.h"
+#include "map.h"
+
+#include <stdint.h>
+#ifndef _MSC_VER
+#include <stdbool.h>
+#endif
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdarg.h>
+
+static void
+set_default_v(void * output, int ctype, pbc_var defv) {
+	switch (ctype) {
+	case CTYPE_INT32:
+		*(uint32_t *)output = defv->integer.low;
+		break;
+	case CTYPE_INT64:
+		*(uint64_t *)output = (uint64_t)defv->integer.low | (uint64_t)defv->integer.hi << 32;
+		break;
+	case CTYPE_DOUBLE:
+		*(double *)output = defv->real;
+		break;
+	case CTYPE_FLOAT:
+		*(float *)output = (float)defv->real;
+		break;
+	case CTYPE_BOOL:
+		*(bool *)output = (defv->integer.low != 0);
+		break;
+	case CTYPE_INT8:
+		*(uint8_t *)output = (uint8_t)defv->integer.low;
+		break;
+	case CTYPE_INT16:
+		*(uint16_t *)output = (uint16_t)defv->integer.low;
+		break;
+	case CTYPE_VAR:
+		*(union _pbc_var *)output = *defv;
+		break;
+	}
+}
+
+static void
+_pattern_set_default(struct _pattern_field *field, char *output) {
+	if (field->ctype == CTYPE_ARRAY || field->ctype == CTYPE_PACKED) {
+		struct _pbc_array *array = (struct _pbc_array *)(output + field->offset);
+		_pbcA_open(array);
+	} else if (field->ptype == PTYPE_ENUM) {
+		pbc_var defv;
+		defv->integer.low = field->defv->e.id;
+		defv->integer.hi = 0;
+		set_default_v(output + field->offset, field->ctype, defv);
+	}
+	set_default_v(output + field->offset, field->ctype, field->defv);
+}
+
+void
+pbc_pattern_set_default(struct pbc_pattern *pat, void *output) {
+	int i;
+	for (i=0;i<pat->count;i++) {
+		_pattern_set_default(&pat->f[i], (char *)output);
+	}
+}
+
+// pattern unpack
+
+static struct _pattern_field *
+bsearch_pattern(struct pbc_pattern *pat, int id)
+{
+	int begin = 0;
+	int end = pat->count;
+	while (begin < end) {
+		int mid = (begin + end)/2;
+		struct _pattern_field * f = &pat->f[mid];
+		if (id == f->id) {
+			return f;
+		}
+		if (id < f->id) {
+			end = mid;
+		} else {
+			begin = mid + 1;
+		}
+	}
+	return NULL;
+}
+
+static inline int
+write_real(int ctype, double v, void *out) {
+	switch(ctype) {
+	case CTYPE_DOUBLE:
+		*(double *)out = v;
+		return 0;
+	case CTYPE_FLOAT:
+		*(float *)out = (float)v;
+		return 0;
+	case CTYPE_VAR:
+		((union _pbc_var *)out)->real = v;
+		return 0;
+	}
+	return -1;
+}
+
+static inline int
+write_longlong(int ctype, struct longlong *i, void *out) {
+	switch(ctype) {
+	case CTYPE_INT32:
+		*(uint32_t *)out = i->low;
+		return 0;
+	case CTYPE_INT64:
+		*(uint64_t *)out = (uint64_t)i->low | (uint64_t)i->hi << 32;
+		return 0;
+	case CTYPE_BOOL:
+		*(bool *)out = (i->low !=0) ;
+		return 0;
+	case CTYPE_INT8:
+		*(uint8_t *)out = (uint8_t)i->low;
+		return 0;
+	case CTYPE_INT16:
+		*(uint8_t *)out = (uint16_t)i->low;
+		return 0;
+	case CTYPE_VAR:
+		((union _pbc_var *)out)->integer = *i;
+		return 0;
+	}
+	return -1;
+}
+
+static inline int
+write_integer(int ctype, struct atom *a, void *out) {
+	return write_longlong(ctype, &(a->v.i), out);
+}
+
+static int unpack_array(int ptype, char *buffer, struct atom *, pbc_array _array);
+
+int
+_pbcP_unpack_packed(uint8_t *buffer, int size, int ptype, pbc_array array) {
+	pbc_var var;
+	var->integer.hi = 0;
+	int i;
+	switch(ptype) {
+	case PTYPE_DOUBLE:
+		if (size % 8 != 0)
+			return -1;
+		for (i=0;i<size;i+=8) {
+			union {
+				double d;
+				uint64_t i64;
+			} u;
+			u.i64 = (uint64_t)buffer[i] |
+				(uint64_t)buffer[i+1] << 8 |
+				(uint64_t)buffer[i+2] << 16 |
+				(uint64_t)buffer[i+3] << 24 |
+				(uint64_t)buffer[i+4] << 32 |
+				(uint64_t)buffer[i+5] << 40 |
+				(uint64_t)buffer[i+6] << 48 |
+				(uint64_t)buffer[i+7] << 56;
+			var->real = u.d;
+			_pbcA_push(array, var);
+		}
+		return size/8;
+	case PTYPE_FLOAT:
+		if (size % 4 != 0)
+			return -1;
+		for (i=0;i<size;i+=4) {
+			union {
+				float f;
+				uint32_t i32;
+			} u;
+			u.i32 = (uint32_t)buffer[i] |
+				(uint32_t)buffer[i+1] << 8 |
+				(uint32_t)buffer[i+2] << 16 |
+				(uint32_t)buffer[i+3] << 24;
+			var->real = (double)u.f;
+			_pbcA_push(array, var);
+		}
+		return size/4;
+	case PTYPE_FIXED32:
+	case PTYPE_SFIXED32:
+		if (size % 4 != 0)
+			return -1;
+		for (i=0;i<size;i+=4) {
+			var->integer.low = (uint32_t)buffer[i] |
+				(uint32_t)buffer[i+1] << 8 |
+				(uint32_t)buffer[i+2] << 16 |
+				(uint32_t)buffer[i+3] << 24;
+			_pbcA_push(array, var);
+		}
+		return size/4;
+	case PTYPE_FIXED64:
+	case PTYPE_SFIXED64:
+		if (size % 8 != 0)
+			return -1;
+		for (i=0;i<size;i+=8) {
+			var->integer.low = (uint32_t)buffer[i] |
+				(uint32_t)buffer[i+1] << 8 |
+				(uint32_t)buffer[i+2] << 16 |
+				(uint32_t)buffer[i+3] << 24;
+			var->integer.hi = (uint32_t)buffer[i+4] |
+				(uint32_t)buffer[i+5] << 8 |
+				(uint32_t)buffer[i+6] << 16 |
+				(uint32_t)buffer[i+7] << 24;
+			_pbcA_push(array, var);
+		}
+		return size/8;
+	case PTYPE_INT64:
+	case PTYPE_UINT64:
+	case PTYPE_INT32:
+	case PTYPE_UINT32:
+	case PTYPE_ENUM:	// enum must be integer type in pattern mode
+	case PTYPE_BOOL: {
+		int n = 0;
+		while (size > 0) {
+			int len;
+			if (size >= 10) {
+				len = _pbcV_decode(buffer, &(var->integer));
+			} else {
+				uint8_t temp[10];
+				memcpy(temp, buffer, size);
+				len = _pbcV_decode(buffer, &(var->integer));
+				if (len > size)
+					return -1;
+			}
+			_pbcA_push(array, var);
+			buffer += len;
+			size -= len;
+			++n;
+		}
+		return n;
+	}
+	case PTYPE_SINT32: {
+		int n = 0;
+		while (size > 0) {
+			int len;
+			if (size >= 10) {
+				len = _pbcV_decode(buffer, &(var->integer));
+				_pbcV_dezigzag32(&(var->integer));
+			} else {
+				uint8_t temp[10];
+				memcpy(temp, buffer, size);
+				len = _pbcV_decode(buffer, &(var->integer));
+				if (len > size)
+					return -1;
+				_pbcV_dezigzag32(&(var->integer));
+			}
+			_pbcA_push(array, var);
+			buffer += len;
+			size -= len;
+			++n;
+		}
+		return n;
+	}
+	case PTYPE_SINT64: {
+		int n = 0;
+		while (size > 0) {
+			int len;
+			if (size >= 10) {
+				len = _pbcV_decode(buffer, &(var->integer));
+				_pbcV_dezigzag64(&(var->integer));
+			} else {
+				uint8_t temp[10];
+				memcpy(temp, buffer, size);
+				len = _pbcV_decode(buffer, &(var->integer));
+				if (len > size)
+					return -1;
+				_pbcV_dezigzag64(&(var->integer));
+			}
+			_pbcA_push(array, var);
+			buffer += len;
+			size -= len;
+			++n;
+		}
+		return n;
+	}
+	}
+	return -1;
+}
+
+static int
+unpack_field(int ctype, int ptype, char * buffer, struct atom * a, void *out) {
+	if (ctype == CTYPE_ARRAY) {
+		return unpack_array(ptype, buffer, a , (struct _pbc_array *)out);
+	}
+	if (ctype == CTYPE_PACKED) {
+		return _pbcP_unpack_packed((uint8_t *)buffer + a->v.s.start, a->v.s.end - a->v.s.start,	ptype, (struct _pbc_array *)out);
+	}
+	switch(ptype) {
+	case PTYPE_DOUBLE:
+		CHECK_BIT64(a, -1);
+		return write_real(ctype, read_double(a), out);
+	case PTYPE_FLOAT:
+		CHECK_BIT32(a, -1);
+		return write_real(ctype, read_float(a), out);
+	case PTYPE_INT64:
+	case PTYPE_UINT64:
+	case PTYPE_INT32:
+	case PTYPE_UINT32:
+	case PTYPE_ENUM:	// enum must be integer type in pattern mode
+	case PTYPE_BOOL:
+		CHECK_VARINT(a, -1);
+		return write_integer(ctype, a , out);
+	case PTYPE_FIXED32:
+	case PTYPE_SFIXED32:
+		CHECK_BIT32(a, -1);
+		return write_integer(ctype, a , out);
+	case PTYPE_FIXED64:
+	case PTYPE_SFIXED64:
+		CHECK_BIT64(a, -1);
+		return write_integer(ctype, a , out);
+	case PTYPE_SINT32: {
+		CHECK_VARINT(a, -1);
+		struct longlong temp = a->v.i;
+		_pbcV_dezigzag32(&temp);
+		return write_longlong(ctype, &temp , out);
+	}
+	case PTYPE_SINT64: {
+		CHECK_LEND(a, -1);
+		struct longlong temp = a->v.i;
+		_pbcV_dezigzag64(&temp);
+		return write_longlong(ctype, &temp , out);
+	}
+	case PTYPE_MESSAGE: 
+		CHECK_LEND(a, -1);
+		((union _pbc_var *)out)->m.buffer = buffer + a->v.s.start;
+		((union _pbc_var *)out)->m.len = a->v.s.end - a->v.s.start;
+		return 0;
+	case PTYPE_STRING:
+	case PTYPE_BYTES:
+		CHECK_LEND(a, -1);
+		((struct pbc_slice *)out)->buffer = buffer + a->v.s.start;
+		((struct pbc_slice *)out)->len = a->v.s.end - a->v.s.start;
+		return 0;
+	}
+	return -1;
+}
+
+static int 
+unpack_array(int ptype, char *buffer, struct atom * a, pbc_array _array) {
+	pbc_var var;
+	int r = unpack_field(CTYPE_VAR, ptype, buffer, a , var);
+	if (r !=0 )
+		return r;
+	_pbcA_push(_array , var);
+
+	return 0;
+}
+
+void 
+pbc_pattern_close_arrays(struct pbc_pattern *pat, void * data) {
+	int i;
+	for (i=0;i<pat->count;i++) {
+		if (pat->f[i].ctype == CTYPE_ARRAY || pat->f[i].ctype == CTYPE_PACKED) {
+			void *array = (char *)data + pat->f[i].offset;
+			_pbcA_close((struct _pbc_array *)array);
+		}
+	}
+}
+
+static inline int
+_pack_wiretype(uint32_t wt, struct pbc_slice *s) {
+	int len;
+	if (s->len < 10) {
+		uint8_t temp[10];
+		len = _pbcV_encode32(wt, temp);
+		if (len > s->len)
+			return -1;
+		memcpy(s->buffer, temp, len);
+	} else {
+		len = _pbcV_encode32(wt, (uint8_t *)s->buffer);
+	}
+	s->buffer = (char *)s->buffer + len;
+	s->len -= len;
+	return len;
+}
+
+static inline int
+_pack_varint64(uint64_t i64, struct pbc_slice *s) {
+	int len;
+	if (s->len < 10) {
+		uint8_t temp[10];
+		len = _pbcV_encode(i64, temp);
+		if (len > s->len)
+			return -1;
+		memcpy(s->buffer, temp, len);
+	} else {
+		len = _pbcV_encode(i64, (uint8_t *)s->buffer);
+	}
+	s->buffer = (char *)s->buffer + len;
+	s->len -= len;
+	return len;
+}
+
+static inline int
+_pack_sint32(uint32_t v, struct pbc_slice *s) {
+	int len;
+	if (s->len < 10) {
+		uint8_t temp[10];
+		len = _pbcV_zigzag32(v, temp);
+		if (len > s->len)
+			return -1;
+		memcpy(s->buffer, temp, len);
+	} else {
+		len = _pbcV_zigzag32(v, (uint8_t *)s->buffer);
+	}
+	s->buffer = (char *)s->buffer + len;
+	s->len -= len;
+	return len;
+}
+
+static inline int
+_pack_sint64(uint64_t v, struct pbc_slice *s) {
+	int len;
+	if (s->len < 10) {
+		uint8_t temp[10];
+		len = _pbcV_zigzag(v, temp);
+		if (len > s->len)
+			return -1;
+		memcpy(s->buffer, temp, len);
+	} else {
+		len = _pbcV_zigzag(v, (uint8_t *)s->buffer);
+	}
+	s->buffer = (char *)s->buffer + len;
+	s->len -= len;
+	return len;
+}
+
+static inline void
+_fix32_encode(uint32_t v , uint8_t *buffer) {
+	buffer[0] = (uint8_t) v;
+	buffer[1] = (uint8_t) (v >> 8);
+	buffer[2] = (uint8_t) (v >> 16);
+	buffer[3] = (uint8_t) (v >> 24);
+}
+
+static inline void
+_fix64_encode(struct longlong *v , uint8_t *buffer) {
+	_fix32_encode(v->low , buffer);
+	_fix32_encode(v->hi, buffer + 4);
+}
+
+static int
+_pack_number(int ptype , int ctype , struct pbc_slice *s, void *input) {
+	pbc_var var;
+	if (ctype == CTYPE_VAR) {
+		memcpy(var, input, sizeof(var));
+	} else {
+		switch (ctype) {
+		case CTYPE_INT32:
+			var->integer.low = *(uint32_t *)input;
+			var->integer.hi = 0;
+			break;
+		case CTYPE_INT64: {
+			uint64_t v = *(uint64_t *)input;
+			var->integer.low = (uint32_t) (v & 0xffffffff);
+			var->integer.hi = (uint32_t) (v >> 32);
+			break;
+		}
+		case CTYPE_INT16:
+			var->integer.low = *(uint16_t *)input;
+			var->integer.hi = 0;
+			break;
+		case CTYPE_INT8:
+			var->integer.low = *(uint8_t *)input;
+			var->integer.hi = 0;
+			break;
+		case CTYPE_BOOL:
+			var->integer.low = *(bool *)input;
+			var->integer.hi = 0;
+			break;
+		case CTYPE_DOUBLE:
+			var->real = *(double *)input;
+			break;
+		case CTYPE_FLOAT:
+			var->real = *(float *)input;
+			break;
+		}
+	}
+
+	switch(ptype) {
+	case PTYPE_FIXED64:
+	case PTYPE_SFIXED64:
+		if (s->len < 8)
+			return -1;
+		_fix64_encode(&(var->integer), (uint8_t *)s->buffer);
+		s->buffer = (char *)s->buffer + 8;
+		s->len -= 8;
+		return 8;
+	case PTYPE_DOUBLE:
+		if (s->len < 8)
+			return -1;
+		double_encode(var->real , (uint8_t *)s->buffer);
+		s->buffer = (char *)s->buffer + 8;
+		s->len -= 8;
+		return 8;
+	case PTYPE_FLOAT:
+		if (s->len < 4)
+			return -1;
+		float_encode((float)var->real , (uint8_t *)s->buffer);
+		s->buffer = (char *)s->buffer + 4;
+		s->len -= 4;
+		return 4;
+	case PTYPE_FIXED32:
+	case PTYPE_SFIXED32:
+		if (s->len < 4)
+			return -1;
+		_fix32_encode(var->integer.low, (uint8_t *)s->buffer);
+		s->buffer = (char *)s->buffer + 4;
+		s->len -= 4;
+		return 4;
+	case PTYPE_UINT64:
+	case PTYPE_INT64:
+	case PTYPE_INT32:
+		return _pack_varint64((uint64_t)var->integer.low | (uint64_t)var->integer.hi << 32, s);
+	case PTYPE_UINT32:
+	case PTYPE_BOOL:
+	case PTYPE_ENUM:
+		return _pack_wiretype(var->integer.low , s);
+	case PTYPE_SINT32: 
+		return _pack_sint32(var->integer.low , s);
+	case PTYPE_SINT64:
+		return _pack_sint64((uint64_t)var->integer.low | (uint64_t)var->integer.hi << 32 , s);
+	default:
+		return -1;
+	}
+}
+
+static int
+_pack_field(struct _pattern_field *pf , int ctype, struct pbc_slice *s, void *input) {
+	int wiretype;
+	int ret = 0;
+	int len;
+	struct pbc_slice * input_slice;
+	struct pbc_slice string_slice;
+
+	switch(pf->ptype) {
+	case PTYPE_FIXED64:
+	case PTYPE_SFIXED64:
+	case PTYPE_DOUBLE:
+		wiretype = WT_BIT64;
+		goto _number;
+	case PTYPE_FIXED32:
+	case PTYPE_SFIXED32:
+	case PTYPE_FLOAT:
+		wiretype = WT_BIT32;
+		goto _number;
+	case PTYPE_UINT64:
+	case PTYPE_INT64:
+	case PTYPE_INT32:
+	case PTYPE_BOOL:
+	case PTYPE_UINT32:
+	case PTYPE_ENUM:
+	case PTYPE_SINT32:
+	case PTYPE_SINT64:
+		wiretype = WT_VARINT;
+		goto _number;
+	case PTYPE_STRING:
+		wiretype = WT_LEND;
+		input_slice = (struct pbc_slice *)input;
+		if (input_slice->len >= 0)
+			goto _string;
+		string_slice.buffer = input_slice->buffer;
+		string_slice.len = strlen((const char *)string_slice.buffer) - input_slice->len;
+		input_slice = &string_slice;
+	
+		goto _string;
+	case PTYPE_MESSAGE:
+	case PTYPE_BYTES:
+		wiretype = WT_LEND;
+		goto _bytes;
+	default:
+		break;
+	}
+
+	return 0;
+_bytes:
+	input_slice = (struct pbc_slice *)input;
+_string:
+	len = _pack_wiretype(pf->id << 3 | WT_LEND , s);
+	if (len < 0) {
+		return len;
+	}
+	ret += len;
+	len = _pack_wiretype(input_slice->len , s);
+	if (len < 0) {
+		return len;
+	}
+	ret += len;
+	if (input_slice->len > s->len)
+		return -1;
+	memcpy(s->buffer , input_slice->buffer, input_slice->len);
+	ret += input_slice->len;
+	s->buffer = (char *)s->buffer + input_slice->len;
+	s->len -= input_slice->len;
+
+	return ret;
+_number:
+	len = _pack_wiretype(pf->id << 3 | wiretype , s);
+	if (len < 0) {
+		return len;
+	}
+	ret += len;
+	len = _pack_number(pf->ptype, ctype , s, input);
+	if (len < 0) {
+		return len;
+	}
+	ret += len;
+
+	return ret;
+}
+
+static int 
+_pack_repeated(struct _pattern_field *pf , struct pbc_slice *s, pbc_array array) {
+	int n = pbc_array_size(array);
+	int ret = 0;
+	if (n>0) {
+		int i;
+		for (i=0;i<n;i++) {
+			int len = _pack_field(pf , CTYPE_VAR , s , _pbcA_index_p(array , i));
+			if (len < 0)
+				return len;
+			ret += len;
+		}
+	}
+	return ret;
+}
+
+static int
+_pack_packed_fixed(struct _pattern_field *pf , int width, struct pbc_slice *s, pbc_array array) {
+	int len;
+	int n = pbc_array_size(array);
+	len = _pack_wiretype(n * width , s);
+	if (len < 0) {
+		return len;
+	}
+	if (s->len - len <  n * width)
+		return -1;
+	int i;
+	for (i=0;i<n;i++) {
+		_pack_number(pf->ptype, CTYPE_VAR , s, _pbcA_index_p(array, i));
+	}
+
+	return len + n * width;
+}
+
+static int
+_pack_packed_varint(struct _pattern_field *pf , struct pbc_slice *slice, pbc_array array) {
+	struct pbc_slice s = * slice;
+	int n = pbc_array_size(array);
+	int estimate = n; 
+	int estimate_len = _pack_wiretype(estimate , &s);
+	if (estimate_len < 0) {
+		return -1;
+	}
+	int i;
+	int packed_len = 0;
+	for (i=0;i<n;i++) {
+		int len	= _pack_number(pf->ptype, CTYPE_VAR , &s, _pbcA_index_p(array, i));
+		if (len < 0)
+			return -1;
+		packed_len += len;
+	}
+	if (packed_len == estimate) {
+		*slice = s;
+		return packed_len + estimate_len;
+	}
+	uint8_t temp[10];
+	struct pbc_slice header_slice = { temp , 10 };
+	int header_len = _pack_wiretype(packed_len , &header_slice);
+	if (header_len == estimate_len) {
+		memcpy(slice->buffer , temp , header_len);
+		*slice = s;
+		return packed_len + estimate_len;
+	}
+	if (header_len + packed_len > slice->len)
+		return -1;
+	memmove((char *)slice->buffer + header_len , (char *)slice->buffer + estimate_len, packed_len);
+	memcpy(slice->buffer , temp , header_len);
+	slice->buffer = (char *)slice->buffer + packed_len + header_len;
+	slice->len -= packed_len + header_len;
+	return packed_len + header_len;
+}
+
+static int 
+_pack_packed(struct _pattern_field *pf , struct pbc_slice *s, pbc_array array) {
+	int n = pbc_array_size(array);
+	if (n == 0)
+		return 0;
+
+	int ret = 0;
+	int len;
+	len = _pack_wiretype(pf->id << 3 | WT_LEND , s);
+	if (len < 0) {
+		return len;
+	}
+	ret += len;
+
+	switch (pf->ptype) {
+	case PTYPE_FIXED64:
+	case PTYPE_SFIXED64:
+	case PTYPE_DOUBLE:
+		len = _pack_packed_fixed(pf, 8, s , array);
+		if (len < 0)
+			return len;
+		break;
+	case PTYPE_FIXED32:
+	case PTYPE_SFIXED32:
+	case PTYPE_FLOAT:
+		len = _pack_packed_fixed(pf, 4, s , array);
+		if (len < 0)
+			return len;
+		break;
+	case PTYPE_UINT64:
+	case PTYPE_INT64:
+	case PTYPE_INT32:
+	case PTYPE_BOOL:
+	case PTYPE_UINT32:
+	case PTYPE_ENUM:
+	case PTYPE_SINT32:
+	case PTYPE_SINT64:
+		len = _pack_packed_varint(pf, s, array);
+		if (len < 0)
+			return len;
+		break;
+	}
+	ret += len;
+
+	return ret;
+}
+
+static bool
+_is_default(struct _pattern_field * pf, void * in) {
+	switch (pf->ctype) {
+	case CTYPE_INT64: {
+		struct longlong * d64 = &pf->defv->integer;
+		return ((uint64_t)d64->low | (uint64_t)d64->hi << 32) == *(uint64_t *)in;
+	}
+	case CTYPE_DOUBLE: 
+		return pf->defv->real == *(double *)in;
+	case CTYPE_FLOAT:
+		return (float)(pf->defv->real) == *(float *)in;
+	case CTYPE_INT32:
+		return pf->defv->integer.low == *(uint32_t *)in;
+	case CTYPE_INT16:
+		return (uint16_t)(pf->defv->integer.low) == *(uint16_t *)in;
+	case CTYPE_INT8:
+		return (uint8_t)(pf->defv->integer.low) == *(uint8_t *)in;
+	case CTYPE_BOOL:
+		if (pf->defv->integer.low)
+			return *(bool *)in == true;
+		else
+			return *(bool *)in == false;
+	}
+	if (pf->ptype == PTYPE_STRING) {
+		struct pbc_slice *slice = (struct pbc_slice *)in;
+		if (slice->buffer == NULL) {
+			return pf->defv->s.str[0] == '\0';
+		}
+		int len = slice->len;
+		if (len <= 0) {
+			return strcmp(pf->defv->s.str, (const char *)slice->buffer) == 0;
+		}
+		return len == pf->defv->s.len && memcmp(pf->defv->s.str, slice->buffer, len)==0;
+	}
+
+	return false;
+}
+
+int 
+pbc_pattern_pack(struct pbc_pattern *pat, void *input, struct pbc_slice * s)
+{
+	struct pbc_slice slice = *s;
+	int i;
+	for (i=0;i<pat->count;i++) {
+		struct _pattern_field * pf = &pat->f[i];
+		void * in = (char *)input + pf->offset;
+		int len = 0;
+		switch(pf->label) {
+		case LABEL_OPTIONAL:
+			if (_is_default(pf , in)) {
+				break;
+			}
+		case LABEL_REQUIRED:
+			len = _pack_field(pf, pf->ctype, &slice, in);
+			break;
+		case LABEL_REPEATED:
+			len = _pack_repeated(pf, &slice , (struct _pbc_array *)in);
+			break;
+		case LABEL_PACKED:
+			len = _pack_packed(pf, &slice , (struct _pbc_array *)in);
+			break;
+		}
+		if (len < 0) {
+			return len;
+		}
+	}
+	int len = (char *)slice.buffer - (char *)s->buffer;
+	int ret = s->len - len;
+	s->len = len;
+	return ret;
+}
+
+int 
+pbc_pattern_unpack(struct pbc_pattern *pat, struct pbc_slice *s, void * output) {
+	if (s->len == 0) {
+		pbc_pattern_set_default(pat, output);
+		return 0;
+	}
+	pbc_ctx _ctx;
+	int r = _pbcC_open(_ctx, s->buffer, s->len);
+	if (r <= 0) {
+		pat->env->lasterror = "Pattern unpack open context error";
+		_pbcC_close(_ctx);
+		return r-1;
+	}
+
+	struct context * ctx = (struct context *)_ctx;
+	bool * field = (bool *)alloca(pat->count * sizeof(bool));
+	memset(field, 0, pat->count * sizeof(bool));
+
+	int i;
+	int fc = 0;
+
+	for (i=0;i<ctx->number;i++) {
+		struct _pattern_field * f = bsearch_pattern(pat, ctx->a[i].wire_id >> 3);
+		if (f) {
+			int index = f - pat->f;
+			if (field[index] == false) {
+				field[index] = true;
+				++fc;
+				if ((f->ctype == CTYPE_ARRAY || f->ctype == CTYPE_PACKED)) {
+					struct _pbc_array *array = (struct _pbc_array *)((char *)output + f->offset);
+					_pbcA_open(array);
+				}
+			}
+			char * out = (char *)output + f->offset;
+			if (unpack_field(f->ctype , f->ptype , ctx->buffer , &ctx->a[i], out) < 0) {
+				int j;
+				for (j=0;j<pat->count;j++) {
+					if (field[j] == true && (pat->f[j].ctype == CTYPE_ARRAY || pat->f[j].ctype == CTYPE_PACKED)) {
+						void *array = (char *)output + pat->f[j].offset;
+						_pbcA_close((struct _pbc_array *)array);
+					}
+				}
+				_pbcC_close(_ctx);
+				pat->env->lasterror = "Pattern unpack field error";
+				return -i-1;
+			}
+		}
+	}
+	_pbcC_close(_ctx);
+	if (fc != pat->count) {
+		for (i=0;i<pat->count;i++) {
+			if (field[i] == false) {
+				_pattern_set_default(&pat->f[i], (char *)output);
+			}
+		}
+	}
+	return 0;
+}
+
+/* 
+	format : key %type
+	%f float
+	%F double
+	%d int32
+	%D int64
+	%b bool
+	%h int16
+	%c int8
+	%s slice
+	%a array
+*/
+
+static int
+_ctype(const char * ctype) {
+	if (ctype[0]!='%')
+		return -1;
+	switch (ctype[1]) {
+	case 'f':
+		return CTYPE_FLOAT;
+	case 'F':
+		return CTYPE_DOUBLE;
+	case 'd':
+		return CTYPE_INT32;
+	case 'D':
+		return CTYPE_INT64;
+	case 'b':
+		return CTYPE_BOOL;
+	case 'h':
+		return CTYPE_INT16;
+	case 'c':
+		return CTYPE_INT8;
+	case 's':
+		return CTYPE_VAR;
+	case 'a':
+		return CTYPE_ARRAY;
+	default:
+		return -1;
+	}
+}
+
+static int
+_ctype_size(const char *ctype) {
+	switch (ctype[1]) {
+	case 'f':
+		return sizeof(float);
+	case 'F':
+		return sizeof(double);
+	case 'd':
+		return sizeof(int32_t);
+	case 'D':
+		return sizeof(int64_t);
+	case 'b':
+		return sizeof(bool);
+	case 'h':
+		return sizeof(int16_t);
+	case 'c':
+		return sizeof(int8_t);
+	case 's':
+		return sizeof(struct pbc_slice);
+	case 'a':
+		return sizeof(pbc_array);
+	default:
+		return 0;
+	}
+}
+
+static const char *
+_copy_string(const char *format , char ** temp) {
+	char * output = *temp;
+	while (*format == ' ' || *format == '\t' || *format == '\n' || *format == '\r') {
+		++format;
+	}
+	while (*format != '\0' &&
+		*format != ' ' &&
+		*format != '\t' &&
+		*format != '\n' &&
+		*format != '\r') {
+		*output = *format;
+		++output;
+		++format;
+	}
+	*output = '\0';
+	++output;
+	*temp = output;
+
+	return format;
+}
+
+static int
+_scan_pattern(const char * format , char * temp) {
+	int n = 0;
+	for(;;) {
+		format = _copy_string(format , &temp);
+		if (format[0] == '\0')
+			return 0;
+		++n;
+		format = _copy_string(format , &temp);
+		if (format[0] == '\0')
+			return n;
+	} 
+}
+
+static int 
+_comp_field(const void * a, const void * b) {
+	const struct _pattern_field * fa = (const struct _pattern_field *)a;
+	const struct _pattern_field * fb = (const struct _pattern_field *)b;
+
+	return fa->id - fb->id;
+}
+
+struct pbc_pattern *
+_pbcP_new(struct pbc_env * env, int n) {
+	size_t sz = sizeof(struct pbc_pattern) + (sizeof(struct _pattern_field)) * (n-1);
+	struct pbc_pattern * ret = (struct pbc_pattern *)malloc(sz);
+	memset(ret, 0 , sz);
+	ret->count = n;
+	ret->env = env;
+	return ret;
+}
+
+static int
+_check_ctype(struct _field * field, struct _pattern_field *f) {
+	if (field->label == LABEL_REPEATED) {
+		return f->ctype != CTYPE_ARRAY;
+	} 
+	if (field->label == LABEL_PACKED) {
+		return f->ctype != CTYPE_PACKED;
+	}
+	if (field->type == PTYPE_STRING || field->type == PTYPE_MESSAGE || field->type == PTYPE_BYTES) {
+		return f->ctype != CTYPE_VAR;
+	}
+	if (field->type == PTYPE_FLOAT || field->type == PTYPE_DOUBLE) {
+		return !(f->ctype == CTYPE_DOUBLE || f->ctype == CTYPE_FLOAT);
+	}
+	if (field->type == PTYPE_ENUM) {
+		return !(f->ctype == CTYPE_INT8 || 
+			f->ctype == CTYPE_INT8 || 
+			f->ctype == CTYPE_INT16 ||
+			f->ctype == CTYPE_INT32 ||
+			f->ctype == CTYPE_INT64);
+	}
+
+	return f->ctype == CTYPE_VAR || f->ctype == CTYPE_ARRAY || f->ctype == CTYPE_PACKED ||
+		f->ctype == CTYPE_DOUBLE || f->ctype == CTYPE_FLOAT;
+}
+
+struct pbc_pattern *
+_pattern_new(struct _message *m, const char *format) {
+	int len = strlen(format);
+	char * temp = (char *)alloca(len+1);
+	int n = _scan_pattern(format, temp);
+	struct pbc_pattern * pat = _pbcP_new(m->env, n);
+	int i;
+
+	const char *ptr = temp;
+
+	int offset = 0;
+
+	for (i=0;i<n;i++) {
+		struct _pattern_field * f = &(pat->f[i]);
+		struct _field * field = (struct _field *)_pbcM_sp_query(m->name, ptr);
+		if (field == NULL) {
+			m->env->lasterror = "Pattern @new query none exist field";
+			goto _error;
+		}
+		f->id = field->id;
+		f->ptype = field->type;
+		*f->defv = *field->default_v;
+		f->offset = offset;
+		f->label = field->label;
+		ptr += strlen(ptr) + 1;
+		f->ctype = _ctype(ptr);
+		if (f->ctype < 0) {
+			m->env->lasterror = "Pattern @new use an invalid ctype";
+			goto _error;
+		}
+		
+		if (f->ctype == CTYPE_ARRAY && field->label == LABEL_PACKED) {
+			f->ctype = CTYPE_PACKED;
+		}
+		if (_check_ctype(field, f)) {
+			m->env->lasterror = "Pattern @new ctype check error";
+			goto _error;
+		}
+
+		offset += _ctype_size(ptr);
+		ptr += strlen(ptr) + 1;
+	}
+
+	pat->count = n;
+
+	qsort(pat->f , n , sizeof(struct _pattern_field), _comp_field);
+	return pat;
+_error:
+	free(pat);
+	return NULL;
+}
+
+struct pbc_pattern * 
+pbc_pattern_new(struct pbc_env * env , const char * message, const char * format, ... ) {
+	struct _message *m = _pbcP_get_message(env, message);
+	if (m==NULL) {
+		env->lasterror = "Pattern new can't find proto";
+		return NULL;
+	}
+	if (format[0]=='@') {
+		return _pattern_new(m , format+1);
+	}
+
+	int len = strlen(format);
+	char * temp = (char *)alloca(len+1);
+	int n = _scan_pattern(format, temp);
+	struct pbc_pattern * pat = _pbcP_new(env, n);
+	int i;
+	va_list ap;
+	va_start(ap , format);
+
+	const char *ptr = temp;
+
+	for (i=0;i<n;i++) {
+		struct _pattern_field * f = &(pat->f[i]);
+		struct _field * field = (struct _field *)_pbcM_sp_query(m->name, ptr);
+		if (field == NULL) {
+			env->lasterror = "Pattern new query none exist field";
+			goto _error;
+		}
+		f->id = field->id;
+		f->ptype = field->type;
+		*f->defv = *field->default_v;
+		f->offset = va_arg(ap, int);
+		f->label = field->label;
+
+		ptr += strlen(ptr) + 1;
+
+		f->ctype = _ctype(ptr);
+		if (f->ctype < 0) {
+			env->lasterror = "Pattern new use an invalid ctype";
+			goto _error;
+		}
+		if (f->ctype == CTYPE_ARRAY && field->label == LABEL_PACKED) {
+			f->ctype = CTYPE_PACKED;
+		}
+		if (_check_ctype(field, f)) {
+			env->lasterror = "Pattern new ctype check error";
+			goto _error;
+		}
+
+		ptr += strlen(ptr) + 1;
+	}
+
+	va_end(ap);
+
+	pat->count = n;
+
+	qsort(pat->f , n , sizeof(struct _pattern_field), _comp_field);
+	return pat;
+_error:
+	free(pat);
+	return NULL;
+}
+
+void 
+pbc_pattern_delete(struct pbc_pattern * pat) {
+	free(pat);
+}
diff --git a/cbits/pattern.h b/cbits/pattern.h
new file mode 100644
--- /dev/null
+++ b/cbits/pattern.h
@@ -0,0 +1,26 @@
+#ifndef PROTOBUF_C_PATTERN_H
+#define PROTOBUF_C_PATTERN_H
+
+#include "pbc.h"
+#include "context.h"
+#include "array.h"
+
+struct _pattern_field {
+	int id;
+	int offset;
+	int ptype;
+	int ctype;
+	int label;
+	pbc_var defv;
+};
+
+struct pbc_pattern {
+	struct pbc_env * env;
+	int count;
+	struct _pattern_field f[1];
+};
+
+struct pbc_pattern * _pbcP_new(struct pbc_env * env, int n);
+int _pbcP_unpack_packed(uint8_t *buffer, int size, int ptype, pbc_array array);
+
+#endif
diff --git a/cbits/pbc-lua.c b/cbits/pbc-lua.c
new file mode 100644
--- /dev/null
+++ b/cbits/pbc-lua.c
@@ -0,0 +1,1098 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+#ifdef __cplusplus
+}
+#endif
+
+#include <stdlib.h>
+
+#ifndef _MSC_VER
+#include <stdbool.h>
+#else
+#define alloca _alloca
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "pbc.h"
+
+#if LUA_VERSION_NUM == 501
+
+#define lua_rawlen lua_objlen
+#define luaL_newlib(L ,reg) luaL_register(L,"protobuf.c",reg)
+#define luaL_buffinit(L , _ ) 
+#define luaL_prepbuffsize( b , cap ) malloc(cap)
+#define _Free(p) free(p)
+#undef luaL_addsize
+#define luaL_addsize(b , len) lua_pushlstring(L, temp , len) ; free(temp)
+#define luaL_pushresult(b) 
+#define luaL_checkversion(L)
+
+#else
+
+#define _Free(p)
+
+#endif
+
+static inline void *
+checkuserdata(lua_State *L, int index) {
+	void * ud = lua_touserdata(L,index);
+	if (ud == NULL) {
+		luaL_error(L, "userdata %d is nil",index);
+	}
+	return ud;
+}
+
+static int
+_env_new(lua_State *L) {
+	struct pbc_env * env = pbc_new();
+	lua_pushlightuserdata(L, env);
+	return 1;
+}
+
+static int
+_env_register(lua_State *L) {
+	struct pbc_env * env = (struct pbc_env *)checkuserdata(L,1);
+	size_t sz = 0;
+	const char * buffer = luaL_checklstring(L, 2 , &sz);
+	struct pbc_slice slice;
+	slice.buffer = (void *)buffer;
+	slice.len = (int)sz;
+	int ret = pbc_register(env, &slice);
+
+	if (ret) {
+		return luaL_error(L, "register fail");
+	}
+	return 0;
+}
+
+static int
+_rmessage_new(lua_State *L) {
+	struct pbc_env * env = (struct pbc_env *)checkuserdata(L,1);
+	const char * type_name = luaL_checkstring(L,2);
+	struct pbc_slice slice;
+	if (lua_isstring(L,3)) {
+		size_t sz = 0;
+		slice.buffer = (void *)lua_tolstring(L,3,&sz);
+		slice.len = (int)sz;
+	} else {
+		slice.buffer = lua_touserdata(L,3);
+		slice.len = luaL_checkinteger(L,4);
+	}
+	struct pbc_rmessage * m = pbc_rmessage_new(env, type_name, &slice);
+	if (m==NULL)
+		return 0;
+	lua_pushlightuserdata(L,m);
+	return 1;
+}
+
+static int
+_rmessage_delete(lua_State *L) {
+	struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
+	pbc_rmessage_delete(m);
+
+	return 0;
+}
+
+static int
+_rmessage_integer(lua_State *L) {
+	struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	int index = luaL_checkinteger(L,3);
+	int32_t v = (int32_t)pbc_rmessage_integer(m, key, index, NULL);
+
+	lua_pushinteger(L,v);
+
+	return 1;
+}
+
+static int
+_rmessage_int32(lua_State *L) {
+	struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	int index = luaL_checkinteger(L,3);
+	uint32_t v = pbc_rmessage_integer(m, key, index, NULL);
+	lua_pushlightuserdata(L,(void *)(intptr_t)v);
+
+	return 1;
+}
+
+
+static int
+_rmessage_int64(lua_State *L) {
+	struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	int index = luaL_checkinteger(L,3);
+	uint32_t v[2];
+	v[0] = pbc_rmessage_integer(m, key, index, &v[1]);
+
+	lua_pushlstring(L,(const char *)v,sizeof(v));
+
+	return 1;
+}
+
+static int
+_rmessage_int52(lua_State *L) {
+	struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	int index = luaL_checkinteger(L,3);
+	uint32_t hi,low;
+	low = pbc_rmessage_integer(m, key, index, &hi);
+	int64_t v = (int64_t)((uint64_t)hi << 32 | (uint64_t)low);
+	lua_pushnumber(L,(lua_Number)v);
+
+	return 1;
+}
+
+static int
+_rmessage_uint52(lua_State *L) {
+	struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	int index = luaL_checkinteger(L,3);
+	uint32_t hi,low;
+	low = pbc_rmessage_integer(m, key, index, &hi);
+	uint64_t v = (uint64_t)hi << 32 | (uint64_t)low;
+	lua_pushnumber(L,(lua_Number)v);
+
+	return 1;
+}
+
+static int 
+_rmessage_real(lua_State *L) {
+	struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	int index = luaL_checkinteger(L,3);
+	double v = pbc_rmessage_real(m, key, index);
+
+	lua_pushnumber(L,v);
+
+	return 1;
+}
+
+static int
+_rmessage_string(lua_State *L) {
+	struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	int index = lua_tointeger(L,3);
+	int sz = 0;
+	const char * v = pbc_rmessage_string(m,key,index,&sz);
+	lua_pushlstring(L,v,sz);
+	return 1;
+}
+
+static int
+_rmessage_message(lua_State *L) {
+	struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	int index = lua_tointeger(L,3);
+	struct pbc_rmessage * v = pbc_rmessage_message(m,key,index);
+	lua_pushlightuserdata(L,v);
+	return 1;
+}
+
+static int
+_rmessage_size(lua_State *L) {
+	struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+
+	int sz = pbc_rmessage_size(m, key);
+
+	lua_pushinteger(L, sz);
+
+	return 1;
+}
+
+static int
+_env_type(lua_State *L) {
+	lua_settop(L,3);
+	struct pbc_env * env = (struct pbc_env *)checkuserdata(L,1);
+	const char * type_name = luaL_checkstring(L,2);
+	if (lua_isnil(L,3)) {
+		int ret = pbc_type(env, type_name, NULL, NULL);
+		lua_pushboolean(L,ret);
+		return 1;
+	}
+	const char * key = luaL_checkstring(L,3);
+	const char * type = NULL;
+	int ret = pbc_type(env, type_name, key, &type);
+	lua_pushinteger(L,ret);
+	if (type == NULL) {
+		return 1;
+	} {
+		lua_pushstring(L, type);
+		return 2;
+	}
+}
+
+static int
+_wmessage_new(lua_State *L) {
+	struct pbc_env * env = (struct pbc_env *)checkuserdata(L,1);
+	const char * type_name = luaL_checkstring(L,2);
+	struct pbc_wmessage * ret = pbc_wmessage_new(env, type_name);
+	lua_pushlightuserdata(L,ret);
+	return 1;
+}
+
+static int
+_wmessage_delete(lua_State *L) {
+	struct pbc_wmessage * m = (struct pbc_wmessage *)lua_touserdata(L,1);
+	pbc_wmessage_delete(m);
+
+	return 0;
+}
+
+
+static int
+_wmessage_integer(lua_State *L) {
+	struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	int number = luaL_checkinteger(L,3);
+	uint32_t hi = 0;
+	if (number < 0)
+		hi = ~0;
+	pbc_wmessage_integer(m, key, number, hi);
+
+	return 0;
+}
+
+static int
+_wmessage_real(lua_State *L) {
+	struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	double number = luaL_checknumber(L,3);
+	pbc_wmessage_real(m, key, number);
+
+	return 0;
+}
+
+static int
+_wmessage_string(lua_State *L) {
+	struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	size_t len = 0;
+	const char * v = luaL_checklstring(L,3,&len);
+	int err = pbc_wmessage_string(m, key, v, (int)len);
+	if (err) {
+		return luaL_error(L, "Write string error : %s", v);
+	}
+
+	return 0;
+}
+
+static int
+_wmessage_message(lua_State *L) {
+	struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	struct pbc_wmessage * ret = pbc_wmessage_message(m, key);
+	lua_pushlightuserdata(L, ret);
+
+	return 1;
+}
+
+static int
+_wmessage_int32(lua_State *L) {
+	struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	if (!lua_islightuserdata(L,3)) {
+		return luaL_error(L,"Need a lightuserdata for int32");
+	}
+	void *number = lua_touserdata(L,3);
+	pbc_wmessage_integer(m, key, (uint32_t)(intptr_t)number , 0);
+	return 0;
+}
+
+static int
+_wmessage_int64(lua_State *L) {
+	struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	switch (lua_type(L,3)) {
+	case LUA_TSTRING : {
+		size_t len = 0;
+		const char * number = lua_tolstring(L,3,&len);
+		if (len !=8 ) {
+			return luaL_error(L,"Need an 8 length string for int64");
+		}
+		const uint32_t * v = (const uint32_t *) number;
+		pbc_wmessage_integer(m, key, v[0] , v[1]);
+		break;
+	}
+	case LUA_TLIGHTUSERDATA : {
+		void * v = lua_touserdata(L,3);
+		uint64_t v64 = (uintptr_t)v;
+		pbc_wmessage_integer(m, key, (uint32_t)v64 , (uint32_t)(v64>>32));
+		break;
+	}
+	default :
+		return luaL_error(L, "Need an int64 type");
+	}
+	return 0;
+}
+
+static int
+_wmessage_int52(lua_State *L) {
+	struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	int64_t number = (int64_t)(luaL_checknumber(L,3));
+	uint32_t hi = (uint32_t)(number >> 32);
+	pbc_wmessage_integer(m, key, (uint32_t)number, hi);
+
+	return 0;
+}
+
+static int
+_wmessage_uint52(lua_State *L) {
+	struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
+	const char * key = luaL_checkstring(L,2);
+	lua_Number v = (luaL_checknumber(L,3));
+	if (v < 0) {
+		return luaL_error(L, "negative number : %f passed to unsigned field",v);
+	}
+	uint64_t number = (uint64_t)v;
+	uint32_t hi = (uint32_t)(number >> 32);
+	pbc_wmessage_integer(m, key, (uint32_t)number, hi);
+
+	return 0;
+}
+
+static int
+_wmessage_buffer(lua_State *L) {
+	struct pbc_slice slice;
+	struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
+	pbc_wmessage_buffer(m , &slice);
+	lua_pushlightuserdata(L, slice.buffer);
+	lua_pushinteger(L, slice.len);
+	return 2;
+}
+
+static int
+_wmessage_buffer_string(lua_State *L) {
+	struct pbc_slice slice;
+	struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
+	pbc_wmessage_buffer(m , &slice);
+	lua_pushlstring(L, (const char *)slice.buffer, slice.len);
+	return 1;
+}
+
+/*
+	lightuserdata env
+ */
+static int
+_last_error(lua_State *L) {
+	struct pbc_env * env = (struct pbc_env *)checkuserdata(L, 1);
+	const char * err = pbc_error(env);
+	lua_pushstring(L,err);
+	return 1;
+}
+
+/*
+	lightuserdata env
+	string message
+	string format
+ */
+static int
+_pattern_new(lua_State *L) {
+	struct pbc_env * env = (struct pbc_env *)checkuserdata(L, 1);
+	const char * message = luaL_checkstring(L,2);
+	const char * format = luaL_checkstring(L,3);
+	struct pbc_pattern * pat = pbc_pattern_new(env, message, format);
+	if (pat == NULL) {
+		return luaL_error(L, "create patten %s (%s) failed", message , format);
+	}
+	lua_pushlightuserdata(L,pat);
+
+	return 1;
+}
+
+static int
+_pattern_delete(lua_State *L) {
+	struct pbc_pattern * pat = (struct pbc_pattern *)lua_touserdata(L,1);
+	pbc_pattern_delete(pat);
+	
+	return 0;
+}
+
+static void *
+_push_value(lua_State *L, char * ptr, char type) {
+	switch(type) {
+		case 'u': {
+			uint64_t v = *(uint64_t*)ptr;
+			ptr += 8;
+			lua_pushnumber(L,(lua_Number)v);
+			break;
+		}
+		case 'i': {
+			int32_t v = *(int32_t*)ptr;
+			ptr += 4;
+			lua_pushinteger(L,v);
+			break;
+		}
+		case 'b': {
+			int32_t v = *(int32_t*)ptr;
+			ptr += 4;
+			lua_pushboolean(L,v);
+			break;
+		}
+		case 'p': {
+			uint32_t v = *(uint32_t*)ptr;
+			ptr += 4;
+			lua_pushlightuserdata(L,(void *)(intptr_t)v);
+			break;
+		}
+		case 'x': {
+			lua_pushlstring(L,ptr,8);
+			ptr += 8;
+			break;
+		}
+		case 'd': {
+			int64_t v = *(int64_t*)ptr;
+			ptr += 8;
+			lua_pushnumber(L,(lua_Number)v);
+			break;
+		}
+		case 'r': {
+			double v = *(double *)ptr;
+			ptr += 8;
+			lua_pushnumber(L,v);
+			break;
+		}
+		case 's': {
+			struct pbc_slice * slice = (struct pbc_slice *)ptr;
+			lua_pushlstring(L,(const char *)slice->buffer, slice->len);
+			ptr += sizeof(struct pbc_slice);
+			break;
+		}
+		case 'm': {
+			struct pbc_slice * slice = (struct pbc_slice *)ptr;
+			lua_createtable(L,2,0);
+			lua_pushlightuserdata(L, slice->buffer);
+			lua_rawseti(L,-2,1);
+			lua_pushinteger(L,slice->len);
+			lua_rawseti(L,-2,2);
+			ptr += sizeof(struct pbc_slice);
+			break;			
+		}
+	}
+	return ptr;
+}
+
+static void
+_push_array(lua_State *L, pbc_array array, char type, int index) {
+	switch (type) {
+	case 'I': {
+		int v = pbc_array_integer(array, index, NULL);
+		lua_pushinteger(L, v);
+		break;
+	}
+	case 'U': {
+		uint32_t hi = 0;
+		uint32_t low = pbc_array_integer(array, index, &hi);
+		uint64_t v = (uint64_t)hi << 32 | (uint64_t)low;
+		lua_pushnumber(L, (lua_Number)v);
+		break;
+	}
+	case 'D': {
+		uint32_t hi = 0;
+		uint32_t low = pbc_array_integer(array, index, &hi);
+		uint64_t v = (uint64_t)hi << 32 | (uint64_t)low;
+		lua_pushnumber(L, (lua_Number)((int64_t)v));
+		break;
+	}
+	case 'B': {
+		int v = pbc_array_integer(array, index, NULL);
+		lua_pushboolean(L, v);
+		break;
+	}
+	case 'P': {
+		uint32_t v = pbc_array_integer(array, index, NULL);
+		lua_pushlightuserdata(L,(void *)(intptr_t)v);
+		break;
+	}
+	case 'X': {
+		uint32_t hi = 0;
+		uint32_t low = pbc_array_integer(array, index, &hi);
+		uint64_t v = (uint64_t)low | (uint64_t)hi << 32;
+		lua_pushlstring(L, (char *)&v, 8);
+		break;
+	}
+	case 'R': {
+		double v = pbc_array_real(array, index);
+		lua_pushnumber(L, v);
+		break;
+	}
+	case 'S': {
+		struct pbc_slice * slice = pbc_array_slice(array, index);
+		lua_pushlstring(L, (const char *)slice->buffer,slice->len);
+		break;
+	}
+	case 'M': {
+		struct pbc_slice * slice = pbc_array_slice(array, index);
+		lua_createtable(L,2,0);
+		lua_pushlightuserdata(L,slice->buffer);
+		lua_rawseti(L,-2,1);
+		lua_pushinteger(L,slice->len);
+		lua_rawseti(L,-2,2);
+		break;
+	}
+	}
+	lua_rawseti(L,-2,index+1);
+}
+
+/*
+	lightuserdata pattern
+	string format "ixrsmb"
+	integer size
+	lightuserdata buffer
+	integer buffer_len
+ */
+static int
+_pattern_unpack(lua_State *L) {
+	struct pbc_pattern * pat = (struct pbc_pattern *)checkuserdata(L, 1);
+	if (pat == NULL) {
+		return luaL_error(L, "unpack pattern is NULL");
+	}
+	size_t format_sz = 0;
+	const char * format = lua_tolstring(L,2,&format_sz);
+	int size = lua_tointeger(L,3);
+	struct pbc_slice slice;
+	if (lua_isstring(L,4)) {
+		size_t buffer_len = 0;
+		const char *buffer = luaL_checklstring(L,4,&buffer_len);
+		slice.buffer = (void *)buffer;
+		slice.len = buffer_len;
+	} else {
+		if (!lua_isuserdata(L,4)) {
+			return luaL_error(L, "Need a userdata");
+		}
+		slice.buffer = lua_touserdata(L,4);
+		slice.len = luaL_checkinteger(L,5);
+	}
+	
+	char * temp = (char *)alloca(size);
+	int ret = pbc_pattern_unpack(pat, &slice, temp);
+	if (ret < 0) {
+		return 0;
+	}
+	lua_checkstack(L, format_sz + 3);
+	int i;
+	char * ptr = temp;
+	bool array = false;
+	for (i=0;i<format_sz;i++) {
+		char type = format[i];
+		if (type >= 'a' && type <='z') {
+			ptr = (char *)_push_value(L,ptr,type);
+		} else {
+			array = true;
+			int n = pbc_array_size((struct _pbc_array *)ptr);
+			lua_createtable(L,n,0);
+			int j;
+			for (j=0;j<n;j++) {
+				_push_array(L,(struct _pbc_array *)ptr, type, j);
+			}
+			ptr += sizeof(pbc_array);
+		}
+	}
+	if (array) {
+		pbc_pattern_close_arrays(pat, temp);
+	}
+	return format_sz;
+}
+
+static char *
+_get_value(lua_State *L, int index, char * ptr, char type) {
+	switch(type) {
+		case 'i': {
+			int32_t v = luaL_checkinteger(L, index);
+			memcpy(ptr, &v, 4);
+			return ptr + 4;
+		}
+		case 'u': {
+			uint64_t v = (uint64_t)luaL_checknumber(L, index);
+			memcpy(ptr, &v, 8);
+			return ptr + 8;
+		}
+		case 'd': {
+			int64_t v = (int64_t)luaL_checknumber(L, index);
+			memcpy(ptr, &v, 8);
+			return ptr + 8;
+		}
+		case 'b': {
+			int32_t v = lua_toboolean(L, index);
+			memcpy(ptr, &v, 4);
+			return ptr + 4;
+		}
+		case 'p': {
+			void *p = lua_touserdata(L, index);
+			uint32_t v = (uint32_t)(intptr_t)p;
+			memcpy(ptr, &v , 4);
+			return ptr + 4;
+		}
+		case 'x': {
+			const char * i64 = luaL_checkstring(L, index);
+			memcpy(ptr, i64, 8);
+			return ptr + 8;
+		}
+		case 'r': {
+			double v = luaL_checknumber(L, index);
+			memcpy(ptr, &v, 8);
+			return ptr + 8;
+		}
+		case 's': {
+			size_t sz = 0;
+			const char * str = luaL_checklstring(L, index, &sz);
+			struct pbc_slice * slice = (struct pbc_slice *)ptr;
+			slice->buffer = (void*)str;
+			slice->len = sz;
+			return ptr + sizeof(struct pbc_slice);
+		}
+		case 'm': {
+			struct pbc_slice * slice = (struct pbc_slice *)ptr;
+			if (lua_istable(L,index)) {
+				lua_rawgeti(L,index,1);
+				slice->buffer = lua_touserdata(L,-1);
+				lua_rawgeti(L,index,2);
+				slice->len = lua_tointeger(L,-1);
+				lua_pop(L,2);
+			} else {
+				size_t sz = 0;
+				const char * buffer = luaL_checklstring(L, index, &sz);
+				slice->buffer = (void *)buffer;
+				slice->len = sz;
+			}
+			return ptr + sizeof(struct pbc_slice);
+		}
+		default:
+			luaL_error(L,"unknown format %c", type);
+			return ptr;
+	}
+}
+
+static void
+_get_array_value(lua_State *L, pbc_array array, char type) {
+	switch(type) {
+		case 'I': {
+			int32_t v = luaL_checkinteger(L, -1);
+			uint32_t hi = 0;
+			if (v<0) {
+				hi = ~0;
+			}
+			pbc_array_push_integer(array, v, hi);
+			break;
+		}
+		case 'U' : {
+			uint64_t v = (uint64_t)luaL_checknumber(L, -1);
+			pbc_array_push_integer(array, (uint32_t)v, (uint32_t)(v >> 32));
+			break;
+		}
+		case 'D' : {
+			int64_t v = (int64_t)luaL_checknumber(L, -1);
+			pbc_array_push_integer(array, (uint32_t)v, (uint32_t)(v >> 32));
+			break;
+		}
+		case 'B': {
+			int32_t v = lua_toboolean(L, -1);
+			pbc_array_push_integer(array, v ? 1: 0, 0);
+			break;
+		}
+		case 'P': {
+			void *p = lua_touserdata(L, -1);
+			uint32_t v = (uint32_t)(intptr_t)p;
+			pbc_array_push_integer(array, v, 0);
+			break;
+		}
+		case 'X': {
+			const char * i64 = luaL_checkstring(L, -1);
+			uint64_t v = *(uint64_t *)i64;
+			pbc_array_push_integer(array, (uint32_t)v, (uint32_t)(v >> 32));
+			break;
+		}
+		case 'R': {
+			double v = luaL_checknumber(L, -1);
+			pbc_array_push_real(array, v);
+			break;
+		}
+		case 'S': {
+			size_t sz = 0;
+			const char * str = luaL_checklstring(L, -1, &sz);
+			struct pbc_slice slice;
+			slice.buffer = (void*)str;
+			slice.len = sz;
+			pbc_array_push_slice(array, &slice);
+			break;
+		}
+		case 'M': {
+			struct pbc_slice slice;
+			if (lua_istable(L,-1)) {
+				lua_rawgeti(L,-1,1);
+				slice.buffer = lua_touserdata(L,-1);
+				lua_rawgeti(L,-2,2);
+				slice.len = lua_tointeger(L,-1);
+				lua_pop(L,2);
+			} else {
+				size_t sz = 0;
+				const char * buffer = luaL_checklstring(L, -1, &sz);
+				slice.buffer = (void *)buffer;
+				slice.len = sz;
+			}
+			pbc_array_push_slice(array, &slice);
+			break;
+		}
+	}
+}
+
+/*
+	lightuserdata pattern
+	string format "ixrsmbp"
+	integer size
+ */
+static int
+_pattern_pack(lua_State *L) {
+	struct pbc_pattern * pat = (struct pbc_pattern *)checkuserdata(L,1);
+	if (pat == NULL) {
+		return luaL_error(L, "pack pattern is NULL");
+	}
+	size_t format_sz = 0;
+	const char * format = lua_tolstring(L,2,&format_sz);
+	int size = lua_tointeger(L,3);
+
+	char * data = (char *)alloca(size);
+//	A trick , we don't need default value. zero buffer for array and message field.
+//	pbc_pattern_set_default(pat, data);
+	memset(data, 0 , size);
+
+	char * ptr = data;
+
+	int i;
+
+	for (i=0;i<format_sz;i++) {
+		if (format[i] >= 'a' && format[i] <='z') {
+			ptr = _get_value(L, 4+i, ptr, format[i]);
+		} else {
+			if (!lua_istable(L,4+i)) {
+				luaL_error(L,"need table for array type");
+			}
+			int j;
+			int n = lua_rawlen(L,4+i);
+			for (j=0;j<n;j++) {
+				lua_rawgeti(L,4+i,j+1);
+				_get_array_value(L,(struct _pbc_array *)ptr,format[i]);
+				lua_pop(L,1);
+			}
+			ptr += sizeof(pbc_array);
+		}
+	}
+
+	luaL_Buffer b;
+	luaL_buffinit(L, &b);
+
+	int cap = 128;
+	for (;;) {
+		char * temp = (char *)luaL_prepbuffsize(&b , cap);
+
+		struct pbc_slice slice;
+		slice.buffer = temp;
+		slice.len = cap;
+
+		int ret = pbc_pattern_pack(pat, data, &slice);
+
+		if (ret < 0) {
+			cap = cap * 2;
+			_Free(temp);
+			continue;
+		}
+
+		luaL_addsize(&b , slice.len);
+		break;
+	}
+	luaL_pushresult(&b);
+
+	pbc_pattern_close_arrays(pat, data);
+	return 1;
+}
+
+static int
+_pattern_size(lua_State *L) {
+	size_t sz =0;
+	const char *format = luaL_checklstring(L,1,&sz);
+	int i;
+	int size = 0;
+	for (i=0;i<sz;i++) {
+		switch(format[i]) {
+		case 'b': 
+		case 'i':
+		case 'p':
+			size += 4;
+			break;
+		case 'r':
+		case 'x': 
+		case 'u':
+		case 'd':
+			size += 8;
+			break;
+		case 's':
+		case 'm':
+			size += sizeof(struct pbc_slice);
+			break;
+		default:
+			size += sizeof(pbc_array);
+			break;
+		}
+	}
+	lua_pushinteger(L,size);
+	return 1;
+}
+
+/*
+	-3 table key
+	-2 table id
+	-1 value
+ */
+static void
+new_array(lua_State *L, int id, const char *key) {
+	lua_rawgeti(L, -2 , id);
+	if (lua_isnil(L, -1)) {
+		lua_pop(L,1);
+		lua_newtable(L);  // table.key table.id value array
+		lua_pushvalue(L,-1);
+		lua_pushvalue(L,-1); // table.key table.id value array array array
+		lua_setfield(L, -6 , key);
+		lua_rawseti(L, -4, id);
+	}
+}
+
+static void
+push_value(lua_State *L, int type, const char * type_name, union pbc_value *v) {
+	switch(type) {
+	case PBC_INT:
+		lua_pushinteger(L, (int)v->i.low);
+		break;
+	case PBC_REAL:
+		lua_pushnumber(L, v->f);
+		break;
+	case PBC_BOOL:
+		lua_pushboolean(L, v->i.low);
+		break;
+	case PBC_ENUM:
+		lua_pushstring(L, v->e.name);
+		break;
+	case PBC_BYTES:
+	case PBC_STRING:
+		lua_pushlstring(L, (const char *)v->s.buffer , v->s.len);
+		break;
+	case PBC_MESSAGE:
+		lua_pushvalue(L, -3);
+		lua_pushstring(L, type_name);
+		lua_pushlstring(L, (const char *)v->s.buffer , v->s.len);
+		lua_call(L, 2 , 1);
+		break;
+	case PBC_FIXED64:
+		lua_pushlstring(L, (const char *)&(v->i), 8);
+		break;
+	case PBC_FIXED32:
+		lua_pushlightuserdata(L,(void *)(intptr_t)v->i.low);
+		break;
+	case PBC_INT64: {
+		uint64_t v64 = (uint64_t)(v->i.hi) << 32 | (uint64_t)(v->i.low);
+		lua_pushnumber(L,(lua_Number)(int64_t)v64);
+		break;
+	}
+	case PBC_UINT: {
+		uint64_t v64 = (uint64_t)(v->i.hi) << 32 | (uint64_t)(v->i.low);
+		lua_pushnumber(L,(lua_Number)v64);
+		break;
+	}
+	default:
+		luaL_error(L, "Unknown type %s", type_name);
+		break;
+	}
+}
+
+/*
+	-3: function decode
+	-2: table key
+	-1:	table id
+ */
+static void
+decode_cb(void *ud, int type, const char * type_name, union pbc_value *v, int id, const char *key) {
+	lua_State *L = (lua_State *)ud;
+	if (key == NULL) {
+		// undefined field
+		return;
+	}
+	if (type & PBC_REPEATED) {
+		push_value(L, type & ~PBC_REPEATED, type_name, v);
+		new_array(L, id , key);	// func.decode table.key table.id value array
+		int n = lua_rawlen(L,-1);
+		lua_insert(L, -2);	// func.decode table.key table.id array value
+		lua_rawseti(L, -2 , n+1);	// func.decode table.key table.id array
+		lua_pop(L,1);
+	} else {
+		push_value(L, type, type_name, v);
+		lua_setfield(L, -3 , key);
+	}
+}
+
+/*
+	:1 lightuserdata env
+	:2 function decode_message
+	:3 table target
+	:4 string type
+	:5 string data
+	:5 lightuserdata pointer
+	:6 integer len
+
+	table
+ */
+static int
+_decode(lua_State *L) {
+	struct pbc_env * env = (struct pbc_env *)checkuserdata(L,1);
+	luaL_checktype(L, 2 , LUA_TFUNCTION);
+	luaL_checktype(L, 3 , LUA_TTABLE);
+	const char * type = luaL_checkstring(L,4);
+	struct pbc_slice slice;
+	if (lua_type(L,5) == LUA_TSTRING) {
+		size_t len;
+		slice.buffer = (void *)luaL_checklstring(L,5,&len);
+		slice.len = (int)len;
+	} else {
+		slice.buffer = checkuserdata(L,5);
+		slice.len = luaL_checkinteger(L,6);
+	}
+	lua_pushvalue(L, 2);
+	lua_pushvalue(L, 3);
+	lua_newtable(L);
+
+	int n = pbc_decode(env, type, &slice, decode_cb, L);
+	if (n<0) {
+		lua_pushboolean(L,0);
+	} else {
+		lua_pushboolean(L,1);
+	}
+	return 1;
+}
+
+struct gcobj {
+	struct pbc_env * env;
+	int size_pat;
+	int cap_pat;
+	struct pbc_pattern ** pat;
+	int size_msg;
+	int cap_msg;
+	struct pbc_rmessage ** msg;
+};
+
+static int
+_clear_gcobj(lua_State *L) {
+	struct gcobj * obj = (struct gcobj *)lua_touserdata(L,1);
+	int i;
+	for (i=0;i<obj->size_pat;i++) {
+		pbc_pattern_delete(obj->pat[i]);
+	}
+	for (i=0;i<obj->size_msg;i++) {
+		pbc_rmessage_delete(obj->msg[i]);
+	}
+	free(obj->pat);
+	free(obj->msg);
+	obj->pat = NULL;
+	obj->msg = NULL;
+	pbc_delete(obj->env);
+	obj->env = NULL;
+
+	return 0;
+}
+
+static int
+_gc(lua_State *L) {
+	struct gcobj * obj = (struct gcobj *)lua_newuserdata(L,sizeof(*obj));
+	obj->env = (struct pbc_env *)lua_touserdata(L,1);
+	obj->size_pat = 0;
+	obj->cap_pat = 4;
+	obj->size_msg = 0;
+	obj->cap_msg = 4;
+	obj->pat = (struct pbc_pattern **)malloc(obj->cap_pat * sizeof(struct pbc_pattern *));
+	obj->msg = (struct pbc_rmessage **)malloc(obj->cap_msg * sizeof(struct pbc_rmessage *));
+
+	lua_createtable(L,0,1);
+	lua_pushcfunction(L, _clear_gcobj);
+	lua_setfield(L,-2,"__gc");
+	lua_setmetatable(L,-2);
+
+	return 1;
+}
+
+static int
+_add_pattern(lua_State *L) {
+	struct gcobj * obj = (struct gcobj *)lua_touserdata(L,1);
+	if (obj->size_pat >= obj->cap_pat) {
+		obj->cap_pat *= 2;
+		obj->pat = (struct pbc_pattern **)realloc(obj->pat, obj->cap_pat * sizeof(struct pbc_pattern *));
+	}
+	struct pbc_pattern * pat = (struct pbc_pattern *)lua_touserdata(L,2);
+	obj->pat[obj->size_pat++] = pat;
+	return 0;
+}
+
+static int
+_add_rmessage(lua_State *L) {
+	struct gcobj * obj = (struct gcobj *)lua_touserdata(L,1);
+	if (obj->size_msg >= obj->cap_msg) {
+		obj->cap_msg *= 2;
+		obj->msg = (struct pbc_rmessage **)realloc(obj->msg, obj->cap_msg * sizeof(struct pbc_rmessage *));
+	}
+	struct pbc_rmessage * msg = (struct pbc_rmessage *)lua_touserdata(L,2);
+	obj->msg[obj->size_msg++] = msg;
+	return 0;
+}
+
+int
+luaopen_protobuf_c(lua_State *L) {
+	luaL_Reg reg[] = {
+		{"_env_new" , _env_new },
+		{"_env_register" , _env_register },
+		{"_env_type", _env_type },
+		{"_rmessage_new" , _rmessage_new },
+		{"_rmessage_delete" , _rmessage_delete },
+		{"_rmessage_integer" , _rmessage_integer },
+		{"_rmessage_int32", _rmessage_int32 },
+		{"_rmessage_int64", _rmessage_int64 },
+		{"_rmessage_int52", _rmessage_int52 },
+		{"_rmessage_uint52", _rmessage_uint52 },
+		{"_rmessage_real" , _rmessage_real },
+		{"_rmessage_string" , _rmessage_string },
+		{"_rmessage_message" , _rmessage_message },
+		{"_rmessage_size" , _rmessage_size },
+		{"_wmessage_new", _wmessage_new },
+		{"_wmessage_delete", _wmessage_delete },
+		{"_wmessage_integer", _wmessage_integer },
+		{"_wmessage_real", _wmessage_real },
+		{"_wmessage_string", _wmessage_string },
+		{"_wmessage_message", _wmessage_message },
+		{"_wmessage_int32", _wmessage_int32 },
+		{"_wmessage_int64", _wmessage_int64 },
+		{"_wmessage_int52", _wmessage_int52 },
+		{"_wmessage_uint52", _wmessage_uint52 },
+		{"_wmessage_buffer", _wmessage_buffer },
+		{"_wmessage_buffer_string", _wmessage_buffer_string },
+		{"_pattern_new", _pattern_new },
+		{"_pattern_delete", _pattern_delete },
+		{"_pattern_size", _pattern_size },
+		{"_pattern_unpack", _pattern_unpack },
+		{"_pattern_pack", _pattern_pack },
+		{"_last_error", _last_error },
+		{"_decode", _decode },
+		{"_gc", _gc },
+		{"_add_pattern", _add_pattern },
+		{"_add_rmessage", _add_rmessage },
+		{NULL,NULL},
+	};
+
+	luaL_checkversion(L);
+	luaL_newlib(L, reg);
+
+	return 1;
+}
diff --git a/cbits/pbc.h b/cbits/pbc.h
new file mode 100644
--- /dev/null
+++ b/cbits/pbc.h
@@ -0,0 +1,104 @@
+#ifndef PROTOBUF_C_H
+#define PROTOBUF_C_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+#define PBC_ARRAY_CAP 64
+
+#define PBC_NOEXIST -1
+#define PBC_INT 1
+#define PBC_REAL 2
+#define PBC_BOOL 3
+#define PBC_ENUM 4
+#define PBC_STRING 5
+#define PBC_MESSAGE 6
+#define PBC_FIXED64 7
+#define PBC_FIXED32 8
+#define PBC_BYTES 9
+#define PBC_INT64 10
+#define PBC_UINT 11
+#define PBC_UNKNOWN 12
+#define PBC_REPEATED 128
+
+typedef struct _pbc_array { char _data[PBC_ARRAY_CAP]; } pbc_array[1];
+
+struct pbc_slice {
+	void *buffer;
+	int len;
+};
+
+struct pbc_pattern;
+struct pbc_env;
+struct pbc_rmessage;
+struct pbc_wmessage;
+
+struct pbc_env * pbc_new(void);
+void pbc_delete(struct pbc_env *);
+int pbc_register(struct pbc_env *, struct pbc_slice * slice);
+int pbc_type(struct pbc_env *, const char * type_name , const char * key , const char ** type);
+const char * pbc_error(struct pbc_env *);
+
+// callback api
+union pbc_value {
+	struct {
+		uint32_t low;
+		uint32_t hi;
+	} i;
+	double f;
+	struct pbc_slice s;
+	struct {
+		int id;
+		const char * name;
+	} e;
+};
+
+typedef void (*pbc_decoder)(void *ud, int type, const char * type_name, union pbc_value *v, int id, const char *key);
+int pbc_decode(struct pbc_env * env, const char * type_name , struct pbc_slice * slice, pbc_decoder f, void *ud);
+
+// message api
+
+struct pbc_rmessage * pbc_rmessage_new(struct pbc_env * env, const char * type_name , struct pbc_slice * slice);
+void pbc_rmessage_delete(struct pbc_rmessage *);
+
+uint32_t pbc_rmessage_integer(struct pbc_rmessage * , const char *key , int index, uint32_t *hi);
+double pbc_rmessage_real(struct pbc_rmessage * , const char *key , int index);
+const char * pbc_rmessage_string(struct pbc_rmessage * , const char *key , int index, int *sz);
+struct pbc_rmessage * pbc_rmessage_message(struct pbc_rmessage *, const char *key, int index);
+int pbc_rmessage_size(struct pbc_rmessage *, const char *key);
+int pbc_rmessage_next(struct pbc_rmessage *, const char **key);
+
+struct pbc_wmessage * pbc_wmessage_new(struct pbc_env * env, const char *type_name);
+void pbc_wmessage_delete(struct pbc_wmessage *);
+
+// for negative integer, pass -1 to hi
+int pbc_wmessage_integer(struct pbc_wmessage *, const char *key, uint32_t low, uint32_t hi);
+int pbc_wmessage_real(struct pbc_wmessage *, const char *key, double v);
+int pbc_wmessage_string(struct pbc_wmessage *, const char *key, const char * v, int len);
+struct pbc_wmessage * pbc_wmessage_message(struct pbc_wmessage *, const char *key);
+void * pbc_wmessage_buffer(struct pbc_wmessage *, struct pbc_slice * slice);
+
+// array api 
+
+int pbc_array_size(pbc_array);
+uint32_t pbc_array_integer(pbc_array array, int index, uint32_t *hi);
+double pbc_array_real(pbc_array array, int index);
+struct pbc_slice * pbc_array_slice(pbc_array array, int index);
+
+void pbc_array_push_integer(pbc_array array, uint32_t low, uint32_t hi);
+void pbc_array_push_slice(pbc_array array, struct pbc_slice *);
+void pbc_array_push_real(pbc_array array, double v);
+
+struct pbc_pattern * pbc_pattern_new(struct pbc_env * , const char * message, const char *format, ...);
+void pbc_pattern_delete(struct pbc_pattern *);
+
+// return unused bytes , -1 for error
+int pbc_pattern_pack(struct pbc_pattern *, void *input, struct pbc_slice * s);
+
+// <0 for error
+int pbc_pattern_unpack(struct pbc_pattern *, struct pbc_slice * s , void * output);
+
+void pbc_pattern_set_default(struct pbc_pattern * , void *data);
+void pbc_pattern_close_arrays(struct pbc_pattern *, void *data);
+
+#endif
diff --git a/cbits/proto.c b/cbits/proto.c
new file mode 100644
--- /dev/null
+++ b/cbits/proto.c
@@ -0,0 +1,252 @@
+#include "pbc.h"
+#include "proto.h"
+#include "pattern.h"
+#include "map.h"
+#include "alloc.h"
+#include "stringpool.h"
+#include "bootstrap.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+const char * 
+pbc_error(struct pbc_env * p) {
+	const char *err = p->lasterror;
+	p->lasterror = "";
+	return err;
+}
+
+struct _message * 
+_pbcP_get_message(struct pbc_env * p , const char *name) {
+	return (struct _message *)_pbcM_sp_query(p->msgs, name);
+}
+
+struct pbc_env * 
+pbc_new(void) {
+	struct pbc_env * p = (struct pbc_env *)malloc(sizeof(*p));
+	p->files = _pbcM_sp_new(0 , NULL);
+	p->enums = _pbcM_sp_new(0 , NULL);
+	p->msgs = _pbcM_sp_new(0 , NULL);
+	p->lasterror = "";
+
+	_pbcB_init(p);
+
+	return p;
+}
+
+static void
+free_enum(void *p) {
+	struct _enum * e = (struct _enum *)p;
+	_pbcM_ip_delete(e->id);
+	_pbcM_si_delete(e->name);
+
+	free(p);
+}
+
+static void
+free_stringpool(void *p) {
+	_pbcS_delete((struct _stringpool *)p);
+}
+
+static void
+free_msg(void *p) {
+	struct _message * m = (struct _message *)p;
+	if (m->id)
+		_pbcM_ip_delete(m->id);
+	free(m->def);
+	_pbcM_sp_foreach(m->name, free);
+	_pbcM_sp_delete(m->name);
+	free(p);
+}
+
+void 
+pbc_delete(struct pbc_env *p) {
+	_pbcM_sp_foreach(p->enums, free_enum);
+	_pbcM_sp_delete(p->enums);
+
+	_pbcM_sp_foreach(p->msgs, free_msg);
+	_pbcM_sp_delete(p->msgs);
+
+	_pbcM_sp_foreach(p->files, free_stringpool);
+	_pbcM_sp_delete(p->files);
+
+	free(p);
+}
+
+struct _enum *
+_pbcP_push_enum(struct pbc_env * p, const char *name, struct map_kv *table, int sz) {
+	void * check = _pbcM_sp_query(p->enums, name);
+	if (check)
+		return NULL;
+	struct _enum * v = (struct _enum *)malloc(sizeof(*v));
+	v->key = name;
+	v->id = _pbcM_ip_new(table,sz);
+	v->name = _pbcM_si_new(table,sz);
+	v->default_v->e.id = table[0].id;
+	v->default_v->e.name = (const char *)table[0].pointer;
+
+	_pbcM_sp_insert(p->enums, name , v);
+	return v;
+}
+
+void 
+_pbcP_push_message(struct pbc_env * p, const char *name, struct _field *f , pbc_array queue) {
+	struct _message * m = (struct _message *)_pbcM_sp_query(p->msgs, name);
+	if (m==NULL) {
+		m = (struct _message *)malloc(sizeof(*m));
+		m->def = NULL;
+		m->key = name;
+		m->id = NULL;
+		m->name = _pbcM_sp_new(0 , NULL);
+		m->env = p;
+		_pbcM_sp_insert(p->msgs, name, m);
+	}
+	struct _field * field = (struct _field *)malloc(sizeof(*field));
+	memcpy(field,f,sizeof(*f));
+	_pbcM_sp_insert(m->name, field->name, field); 
+	pbc_var atom;
+	atom->m.buffer = field;
+	if (f->type == PTYPE_MESSAGE || f->type == PTYPE_ENUM) {
+		_pbcA_push(queue, atom);
+	}
+}
+
+struct _iter {
+	int count;
+	struct map_kv * table;
+};
+
+static void
+_count(void *p, void *ud) {
+	struct _iter *iter = (struct _iter *)ud;
+	iter->count ++;
+}
+
+static void
+_set_table(void *p, void *ud) {
+	struct _field * field = (struct _field *)p;
+	struct _iter *iter = (struct _iter *)ud;
+	iter->table[iter->count].id = field->id;
+	iter->table[iter->count].pointer = field;
+	++iter->count;
+}
+
+struct _message * 
+_pbcP_init_message(struct pbc_env * p, const char *name) {
+	struct _message * m = (struct _message *)_pbcM_sp_query(p->msgs, name);
+	if (m == NULL) {
+		m = (struct _message *)malloc(sizeof(*m));
+		m->def = NULL;
+		m->key = name;
+		m->id = NULL;
+		m->name = _pbcM_sp_new(0 , NULL);
+		m->env = p;
+		_pbcM_sp_insert(p->msgs, name, m);
+
+		return m;
+	}
+	if (m->id) {
+		// extend message, delete old id map.
+		_pbcM_ip_delete(m->id);
+	}
+	struct _iter iter = { 0, NULL };
+	_pbcM_sp_foreach_ud(m->name, _count, &iter);
+	iter.table = (struct map_kv *)malloc(iter.count * sizeof(struct map_kv));
+	iter.count = 0;
+	_pbcM_sp_foreach_ud(m->name, _set_table, &iter);
+
+	m->id = _pbcM_ip_new(iter.table , iter.count);
+
+	free(iter.table);
+
+	return m;
+}
+
+int 
+_pbcP_message_default(struct _message * m, const char * name, pbc_var defv) {
+	struct _field * f= (struct _field *)_pbcM_sp_query(m->name, name);
+	if (f==NULL) {
+		// invalid key
+		defv->p[0] = NULL;
+		defv->p[1] = NULL;
+		return -1;
+	}
+	*defv = *(f->default_v);
+	return f->type;
+}
+
+int 
+_pbcP_type(struct _field * field, const char ** type) {
+	if (field == NULL) {
+		return 0;
+	}
+	int ret = 0;
+	switch (field->type) {
+	case PTYPE_DOUBLE:
+	case PTYPE_FLOAT:
+		ret = PBC_REAL;
+		break;
+	case PTYPE_INT64:
+	case PTYPE_SINT64:  
+		ret = PBC_INT64;
+		break;
+	case PTYPE_INT32:
+	case PTYPE_SINT32:  
+		ret = PBC_INT;
+		break;
+	case PTYPE_UINT32:
+	case PTYPE_UINT64:
+		ret = PBC_UINT;
+		break;
+	case PTYPE_FIXED32:
+	case PTYPE_SFIXED32:
+		ret = PBC_FIXED32;
+		break;
+	case PTYPE_SFIXED64:
+	case PTYPE_FIXED64:
+		ret = PBC_FIXED64;
+		break;
+	case PTYPE_BOOL:
+		ret = PBC_BOOL;
+		break;
+	case PTYPE_STRING:
+		ret = PBC_STRING;
+		break;
+	case PTYPE_BYTES:  
+		ret = PBC_BYTES;
+		break;
+	case PTYPE_ENUM:
+		ret = PBC_ENUM;
+		if (type) {
+			*type = field->type_name.e->key;
+		}
+		break;
+	case PTYPE_MESSAGE:
+		ret = PBC_MESSAGE;
+		if (type) {
+			*type = field->type_name.m->key;
+		}
+		break;
+	default:
+		return 0;
+	}
+	if (field->label == LABEL_REPEATED ||
+		field->label == LABEL_PACKED) {
+		ret |= PBC_REPEATED;
+	}
+
+	return ret;
+}
+
+int 
+pbc_type(struct pbc_env * p, const char * type_name , const char * key , const char ** type) {
+	struct _message *m = _pbcP_get_message(p, type_name);
+	if (m==NULL) {
+		return 0;
+	}
+	if (key == NULL) {
+		return PBC_NOEXIST;
+	}
+	struct _field * field = (struct _field *)_pbcM_sp_query(m->name, key);
+	return _pbcP_type(field, type);
+}
diff --git a/cbits/proto.h b/cbits/proto.h
new file mode 100644
--- /dev/null
+++ b/cbits/proto.h
@@ -0,0 +1,65 @@
+#ifndef PROTOBUFC_PROTO_H
+#define PROTOBUFC_PROTO_H
+
+#include "pbc.h"
+#include "map.h"
+#include "array.h"
+#ifndef _MSC_VER
+#include <stdbool.h>
+#endif
+#include <stddef.h>
+
+struct map_ip;
+struct map_si;
+struct map_sp;
+struct _message;
+struct _enum;
+
+#define LABEL_OPTIONAL 0
+#define LABEL_REQUIRED 1
+#define LABEL_REPEATED 2
+#define LABEL_PACKED 3
+
+struct _field {
+	int id;
+	const char *name;
+	int type;
+	int label;
+	pbc_var default_v;
+	union {
+		const char * n;
+		struct _message * m;
+		struct _enum * e;
+	} type_name;
+};
+
+struct _message {
+	const char * key;
+	struct map_ip * id;	// id -> _field
+	struct map_sp * name;	// string -> _field
+	struct pbc_rmessage * def;	// default message
+	struct pbc_env * env;
+};
+
+struct _enum {
+	const char * key;
+	struct map_ip * id;
+	struct map_si * name;
+	pbc_var default_v;
+};
+
+struct pbc_env {
+	struct map_sp * files;	// string -> void *
+	struct map_sp * enums;	// string -> _enum
+	struct map_sp * msgs;	// string -> _message
+	const char * lasterror;
+};
+
+struct _message * _pbcP_init_message(struct pbc_env * p, const char *name);
+void _pbcP_push_message(struct pbc_env * p, const char *name, struct _field *f , pbc_array queue);
+struct _enum * _pbcP_push_enum(struct pbc_env * p, const char *name, struct map_kv *table, int sz );
+int _pbcP_message_default(struct _message * m, const char * name, pbc_var defv);
+struct _message * _pbcP_get_message(struct pbc_env * p, const char *name);
+int _pbcP_type(struct _field * field, const char **type);
+
+#endif
diff --git a/cbits/register.c b/cbits/register.c
new file mode 100644
--- /dev/null
+++ b/cbits/register.c
@@ -0,0 +1,342 @@
+#include "pbc.h"
+#include "proto.h"
+#include "alloc.h"
+#include "map.h"
+#include "bootstrap.h"
+#include "context.h"
+#include "stringpool.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef _MSC_VER
+#define strtoll _strtoi64
+#endif
+
+static const char *
+_concat_name(struct _stringpool *p , const char *prefix ,  int prefix_sz , const char *name , int name_sz, int *sz) {
+	if (prefix_sz == 0) {
+		if (sz) {
+			*sz = name_sz;
+		}
+		return _pbcS_build(p , name, name_sz);
+	}
+	char * temp = (char *)alloca(name_sz + prefix_sz + 2);
+	memcpy(temp,prefix,prefix_sz);
+	temp[prefix_sz] = '.';
+	memcpy(temp+prefix_sz+1,name,name_sz);
+	temp[name_sz + prefix_sz + 1] = '\0';
+	if (sz) {
+		*sz = name_sz + prefix_sz + 1;
+	}
+	const char * ret = _pbcS_build(p , temp, name_sz + prefix_sz + 1);
+	return ret;
+}
+
+static void
+_register_enum(struct pbc_env *p, struct _stringpool *pool, struct pbc_rmessage * enum_type, const char *prefix, int prefix_sz) {
+	int field_count = pbc_rmessage_size(enum_type, "value");
+	struct map_kv *table = (struct map_kv *)malloc(field_count * sizeof(struct map_kv));
+	int i;
+	for (i=0;i<field_count;i++) {
+		struct pbc_rmessage * value = pbc_rmessage_message(enum_type, "value", i);
+		int enum_name_sz;
+		const char *enum_name = pbc_rmessage_string(value , "name" , 0 , &enum_name_sz);
+		table[i].pointer = (void *)_pbcS_build(pool, enum_name , enum_name_sz);
+		table[i].id = pbc_rmessage_integer(value , "number", 0 , 0);
+	}
+	int name_sz;
+	const char * name = pbc_rmessage_string(enum_type, "name", 0 , &name_sz);
+	const char *temp = _concat_name(pool, prefix , prefix_sz , name , name_sz, NULL);
+
+	_pbcP_push_enum(p,temp,table,field_count);
+	free(table);
+}
+
+static void
+_set_default(struct _stringpool *pool, struct _field *f , int ptype, const char *value, int sz) {
+	if (value == NULL || sz == 0) {
+		if (f->type == PTYPE_STRING || f->type == PTYPE_BYTES) {
+			f->default_v->s.str = "";
+			f->default_v->s.len = 0;
+		} else {
+			f->default_v->integer.low = 0;
+			f->default_v->integer.hi = 0;
+		}
+		return;
+	}
+
+	switch (f->type) {
+	case PTYPE_DOUBLE:
+	case PTYPE_FLOAT:
+		f->default_v->real = strtod(value,NULL);
+		break;
+	case PTYPE_STRING:
+		f->default_v->s.str = _pbcS_build(pool, value , sz);
+		f->default_v->s.len = sz;
+		break;
+	case PTYPE_ENUM:
+		// enum default value will be converted to f->default_v->e in bootstrap.c : set_field_one()
+		f->default_v->s.str = value;
+		f->default_v->s.len = sz;
+		break;
+	case PTYPE_BOOL:
+		if (strcmp(value,"true") == 0) {
+			f->default_v->integer.low = 1;
+		} else {
+			f->default_v->integer.low = 0;
+		}
+		f->default_v->integer.hi = 0;
+		break;
+	case PTYPE_UINT64:
+	case PTYPE_INT64:
+	case PTYPE_SFIXED64:
+	case PTYPE_SINT64: {
+		long long v = strtoll(value, NULL, 10);
+		f->default_v->integer.low = (long) v;
+		f->default_v->integer.hi = (long)(v >> 32);
+		break;
+		}
+	case PTYPE_INT32:
+	case PTYPE_FIXED32:
+	case PTYPE_SFIXED32:
+	case PTYPE_SINT32: {
+		int low = strtol(value, NULL, 10);
+		f->default_v->integer.low = low;
+		if (low < 0) {
+			f->default_v->integer.hi = -1;
+		} else {
+			f->default_v->integer.hi = 0;
+		}
+		break;
+	}
+	case PTYPE_UINT32:
+		f->default_v->integer.low = strtoul(value, NULL, 10);
+		f->default_v->integer.hi = 0;
+		break;
+	case PTYPE_BYTES:
+	case PTYPE_MESSAGE:
+		// bytes and message types have no default value
+		f->default_v->m.buffer = 0;
+		f->default_v->m.len = 0;
+		break;
+	default:
+		f->default_v->integer.low = 0;
+		f->default_v->integer.hi = 0;
+		break;
+	}
+}
+
+static void
+_register_field(struct pbc_rmessage * field, struct _field * f, struct _stringpool *pool) {
+	f->id = pbc_rmessage_integer(field, "number", 0 , 0);
+	f->type = pbc_rmessage_integer(field, "type", 0 , 0);	// enum
+	f->label = pbc_rmessage_integer(field, "label", 0, 0) - 1; // LABEL_OPTIONAL = 0
+	if (pbc_rmessage_size(field , "options") > 0) {
+		struct pbc_rmessage * options = pbc_rmessage_message(field, "options" , 0);
+		int packed = pbc_rmessage_integer(options , "packed" , 0 , NULL);
+		if (packed) {
+			f->label = LABEL_PACKED;
+		}
+	}
+	f->type_name.n = pbc_rmessage_string(field, "type_name", 0 , NULL) +1;	// abandon prefix '.' 
+	int vsz;
+	const char * default_value = pbc_rmessage_string(field, "default_value", 0 , &vsz);
+	_set_default(pool , f , f->type, default_value , vsz);
+}
+
+static void
+_register_extension(struct pbc_env *p, struct _stringpool *pool , const char * prefix, int prefix_sz, struct pbc_rmessage * msg, pbc_array queue) {
+	int extension_count = pbc_rmessage_size(msg , "extension");
+	if (extension_count <= 0) 
+		return;
+	int i;
+
+	const char * last = NULL;
+
+	for (i=0;i<extension_count;i++) {
+		struct pbc_rmessage * extension = pbc_rmessage_message(msg, "extension", i);
+		int field_name_sz = 0;
+		struct _field f;
+		const char * field_name = pbc_rmessage_string(extension , "name" , 0, &field_name_sz);
+		f.name =  _concat_name(pool, prefix, prefix_sz, field_name, field_name_sz, NULL);
+
+		_register_field(extension, &f , pool);
+
+		const char * extendee = pbc_rmessage_string(extension , "extendee" , 0, NULL);
+
+		_pbcP_push_message(p, extendee + 1 , &f , queue);
+
+		if (last == NULL) {
+			last = extendee;
+		} else if (strcmp(extendee,last) != 0) {
+			_pbcP_init_message(p, last+1);
+			last = extendee;
+		} 
+	}
+	_pbcP_init_message(p, last+1);
+}
+
+static void
+_register_message(struct pbc_env *p, struct _stringpool *pool, struct pbc_rmessage * message_type, const char *prefix, int prefix_sz, pbc_array queue) {
+	int name_sz;
+	const char * name = pbc_rmessage_string(message_type, "name", 0 , &name_sz);
+	int sz = 0;
+	const char *temp = _concat_name(pool, prefix , prefix_sz , name , name_sz, &sz);
+
+	int field_count = pbc_rmessage_size(message_type, "field");
+	int i;
+	for (i=0;i<field_count;i++) {
+		struct pbc_rmessage * field = pbc_rmessage_message(message_type, "field" , i);
+		struct _field f;
+		int field_name_sz;
+		const char * field_name = pbc_rmessage_string(field, "name", 0 , &field_name_sz);
+		f.name = _pbcS_build(pool,field_name,field_name_sz);
+
+		_register_field(field, &f , pool);
+
+		_pbcP_push_message(p, temp , &f , queue);
+	}
+
+	_pbcP_init_message(p, temp);
+
+	_register_extension(p, pool, temp, sz,message_type, queue);
+
+	// nested enum
+
+	int enum_count = pbc_rmessage_size(message_type, "enum_type");
+
+	for (i=0;i<enum_count;i++) {
+		struct pbc_rmessage * enum_type = pbc_rmessage_message(message_type, "enum_type", i);
+		_register_enum(p, pool, enum_type, temp, sz);
+	}
+	
+	// nested type
+	int message_count = pbc_rmessage_size(message_type, "nested_type");
+	for (i=0;i<message_count;i++) {
+		struct pbc_rmessage * nested_type = pbc_rmessage_message(message_type, "nested_type", i);
+		_register_message(p, pool, nested_type, temp, sz, queue);
+	}
+}
+
+static void
+_register(struct pbc_env *p, struct pbc_rmessage * file, struct _stringpool *pool) {
+	int package_sz;
+	const char *package = pbc_rmessage_string(file, "package", 0, &package_sz);
+
+	pbc_array queue;
+	_pbcA_open(queue);
+
+	int enum_count = pbc_rmessage_size(file, "enum_type");
+	int i;
+
+	for (i=0;i<enum_count;i++) {
+		struct pbc_rmessage * enum_type = pbc_rmessage_message(file, "enum_type", i);
+		_register_enum(p,  pool , enum_type, package, package_sz);
+	}
+
+	int message_count = pbc_rmessage_size(file, "message_type");
+	for (i=0;i<message_count;i++) {
+		struct pbc_rmessage * message_type = pbc_rmessage_message(file, "message_type", i);
+		_register_message(p, pool, message_type, package, package_sz, queue);
+	}
+
+	_register_extension(p, pool, package, package_sz, file , queue);
+
+	_pbcB_register_fields(p, queue);
+
+	_pbcA_close(queue);
+}
+
+#define CHECK_FILE_OK 0
+#define CHECK_FILE_EXIST 1
+#define CHECK_FILE_DEPENDENCY 2
+
+static int
+_check_file_name(struct pbc_env * p , struct pbc_rmessage * file, const char ** fname) {
+	const char * filename = pbc_rmessage_string(file, "name", 0, NULL);
+//	printf("reg :%s\n",filename);
+	if (_pbcM_sp_query(p->files, filename)) {
+		return CHECK_FILE_EXIST;
+	}
+	int sz = pbc_rmessage_size(file, "dependency"); 
+	int i;
+	for (i=0;i<sz;i++) {
+		const char *dname = pbc_rmessage_string(file,"dependency",i,NULL);
+//		printf("dependency :%s\n",dname);
+		if (_pbcM_sp_query(p->files, dname) == NULL) {
+			return CHECK_FILE_DEPENDENCY;
+		}
+	}
+
+	*fname = filename;
+
+	return CHECK_FILE_OK;
+}
+
+static int
+_register_no_dependency(struct pbc_env * p,struct pbc_rmessage ** files , int n ) {
+	int r = 0;
+	int i;
+	for (i=0;i<n;i++) {
+		if (files[i] == NULL)
+			continue;
+		const char *filename = NULL;
+		int err = _check_file_name(p, files[i], &filename);
+		switch(err) {
+		case CHECK_FILE_EXIST:
+			break;
+		case CHECK_FILE_DEPENDENCY:
+			++r;
+			break;
+		case CHECK_FILE_OK: {
+			struct _stringpool *pool = _pbcS_new();
+			filename = _pbcS_build(pool, filename , strlen(filename));
+			_pbcM_sp_insert(p->files , filename, pool);
+			_register(p,files[i],pool);
+			files[i] = NULL;
+			}
+			break;
+		}
+	}
+	return r;
+}
+
+int
+pbc_register(struct pbc_env * p, struct pbc_slice *slice) {
+	struct pbc_rmessage * message = pbc_rmessage_new(p, "google.protobuf.FileDescriptorSet", slice);
+	if (message == NULL) {
+		p->lasterror = "register open google.protobuf.FileDescriptorSet fail";
+		return 1;
+	}
+	int n = pbc_rmessage_size(message, "file");
+	struct pbc_rmessage ** files = (struct pbc_rmessage **)alloca(n * sizeof(struct pbc_rmessage *));
+	int i;
+	if (n == 0) {
+		p->lasterror = "register empty";
+		goto _error;
+	}
+	for (i=0;i<n;i++) {
+		files[i] = pbc_rmessage_message(message, "file", i);
+		if (files[i] == NULL) {
+			p->lasterror = "register open fail";
+			goto _error;
+		}
+	}
+
+	int r = n;
+	do {
+		int rr = _register_no_dependency(p,files , n);
+		if (rr == r) {
+			p->lasterror = "register dependency error";
+			goto _error;
+		}
+		r = rr;
+	} while (r>0);
+
+	pbc_rmessage_delete(message);
+	return 0;
+_error:
+	pbc_rmessage_delete(message);
+	return 1;
+}
diff --git a/cbits/rmessage.c b/cbits/rmessage.c
new file mode 100644
--- /dev/null
+++ b/cbits/rmessage.c
@@ -0,0 +1,455 @@
+#include "pbc.h"
+#include "alloc.h"
+#include "map.h"
+#include "context.h"
+#include "proto.h"
+#include "pattern.h"
+#include "varint.h"
+
+#include <stddef.h>
+#include <string.h>
+
+struct pbc_rmessage {
+		struct _message * msg;
+		struct map_sp * index;	// key -> struct value *
+		struct heap * heap;
+};
+
+union _var {
+	pbc_var var;
+	pbc_array array;
+	struct pbc_rmessage message;
+} ;
+
+struct value {
+	struct _field * type;
+	union _var v;
+};
+
+int 
+pbc_rmessage_next(struct pbc_rmessage *m, const char **key) {
+	struct value * v = (struct value *)_pbcM_sp_next(m->index, key);
+	if (*key == NULL) {
+		return 0;
+	}
+	return _pbcP_type(v->type, NULL);
+}
+
+#define SIZE_VAR (offsetof(struct value, v) + sizeof(pbc_var))
+#define SIZE_ARRAY (offsetof(struct value, v) + sizeof(pbc_array))
+#define SIZE_MESSAGE (offsetof(struct value, v) + sizeof(struct pbc_rmessage))
+
+static struct value *
+read_string(struct heap *h, struct atom *a,struct _field *f, uint8_t *buffer) {
+	const char * temp = (const char *) (buffer + a->v.s.start);
+	int len = a->v.s.end - a->v.s.start;
+
+	if (len > 0 && temp[len-1] == '\0') {
+		struct value * v = (struct value *)_pbcH_alloc(h, SIZE_VAR);
+		v->v.var->s.str = temp;
+		v->v.var->s.len = len;
+		return v;
+	} else {
+		struct value * v = (struct value *)_pbcH_alloc(h, SIZE_VAR + len + 1);
+		memcpy(((char *)v) + SIZE_VAR , temp, len);
+		*(((char *)v) + SIZE_VAR + len) = '\0';
+		v->v.var->s.str = ((char *)v) + SIZE_VAR;
+		v->v.var->s.len = len;
+		return v;
+	}
+}
+
+static void
+read_string_var(struct heap *h, pbc_var var,struct atom *a,struct _field *f,uint8_t *buffer) {
+	const char * temp = (const char *) (buffer + a->v.s.start);
+	int len = a->v.s.end - a->v.s.start;
+	if (len == 0) {
+		var->s.str = "";
+		var->s.len = 0;
+	}
+	else if (temp[len-1] == '\0') {
+		var->s.str = temp;
+		var->s.len = len;
+	} else {
+		char * temp2 = (char *)_pbcH_alloc(h, len + 1);
+		memcpy(temp2, temp, len);
+		temp2[len]='\0';
+		var->s.str = temp2;
+		var->s.len = -len;
+	}
+}
+
+static void _pbc_rmessage_new(struct pbc_rmessage * ret , struct _message * type ,  void *buffer, int size, struct heap *h);
+
+static struct value *
+read_value(struct heap *h, struct _field *f, struct atom * a, uint8_t *buffer) {
+	struct value * v;
+
+	switch (f->type) {
+	case PTYPE_DOUBLE:
+		CHECK_BIT64(a,NULL);
+		v = (struct value *)_pbcH_alloc(h, SIZE_VAR);
+		v->v.var->real = read_double(a);
+		break;
+	case PTYPE_FLOAT:
+		CHECK_BIT32(a,NULL);
+		v = (struct value *)_pbcH_alloc(h, SIZE_VAR);
+		v->v.var->real = (double) read_float(a);
+		break;
+	case PTYPE_ENUM:
+		CHECK_VARINT(a,NULL);
+		v = (struct value *)_pbcH_alloc(h, SIZE_VAR);
+		v->v.var->e.id = a->v.i.low;
+		v->v.var->e.name = (const char *)_pbcM_ip_query(f->type_name.e->id , a->v.i.low);
+		break;
+	case PTYPE_INT64:
+	case PTYPE_UINT64:
+	case PTYPE_INT32:
+	case PTYPE_UINT32:
+	case PTYPE_BOOL:
+		CHECK_VARINT(a,NULL);
+		v = (struct value *)_pbcH_alloc(h, SIZE_VAR);
+		v->v.var->integer = a->v.i;
+		break;
+	case PTYPE_FIXED32:
+	case PTYPE_SFIXED32:
+		CHECK_BIT32(a,NULL);
+		v = (struct value *)_pbcH_alloc(h, SIZE_VAR);
+		v->v.var->integer = a->v.i;
+		break;
+	case PTYPE_FIXED64:
+	case PTYPE_SFIXED64:
+		CHECK_BIT64(a,NULL);
+		v = (struct value *)_pbcH_alloc(h, SIZE_VAR);
+		v->v.var->integer = a->v.i;
+		break;
+	case PTYPE_SINT32: 
+		CHECK_VARINT(a,NULL);
+		v = (struct value *)_pbcH_alloc(h, SIZE_VAR);
+		v->v.var->integer = a->v.i;
+		_pbcV_dezigzag32(&(v->v.var->integer));
+		break;
+	case PTYPE_SINT64:
+		CHECK_VARINT(a,NULL);
+		v = (struct value *)_pbcH_alloc(h, SIZE_VAR);
+		v->v.var->integer = a->v.i;
+		_pbcV_dezigzag64(&(v->v.var->integer));
+		break;
+	case PTYPE_STRING:
+		CHECK_LEND(a,NULL);
+		v = read_string(h,a,f,buffer);
+		break;
+	case PTYPE_BYTES:
+		CHECK_LEND(a,NULL);
+		v = (struct value *)_pbcH_alloc(h, SIZE_VAR);
+		v->v.var->s.str = (const char *)(buffer + a->v.s.start);
+		v->v.var->s.len = a->v.s.end - a->v.s.start;
+		break;
+	case PTYPE_MESSAGE:
+		CHECK_LEND(a,NULL);
+		v = (struct value *)_pbcH_alloc(h, SIZE_MESSAGE);
+		_pbc_rmessage_new(&(v->v.message), f->type_name.m , 
+			buffer + a->v.s.start , 
+			a->v.s.end - a->v.s.start,h);
+		break;
+	default:
+		return NULL;
+	}
+	v->type = f;
+	return v;
+}
+
+static void
+push_value_packed(struct _message * type, pbc_array array, struct _field *f, struct atom * aa, uint8_t *buffer) {
+	int n = _pbcP_unpack_packed((uint8_t *)buffer + aa->v.s.start, aa->v.s.end - aa->v.s.start,
+		f->type , array);
+	if (n<=0) {
+		// todo  : error
+		type->env->lasterror = "Unpack packed field error";
+		return;
+	}
+	if (f->type == PTYPE_ENUM) {
+		int i;
+		for (i=0;i<n;i++) {
+			union _pbc_var * v = (union _pbc_var *)_pbcA_index_p(array, i);
+			int id = v->integer.low;
+			v->e.id = id;
+			v->e.name = (const char*)_pbcM_ip_query(f->type_name.e->id , id);
+		}
+	}
+}
+
+static void
+push_value_array(struct heap *h, pbc_array array, struct _field *f, struct atom * a, uint8_t *buffer) {
+	pbc_var v;
+
+	switch (f->type) {
+	case PTYPE_DOUBLE:
+		v->real = read_double(a);
+		break;
+	case PTYPE_FLOAT:
+		v->real = (double) read_float(a);
+		break;
+	case PTYPE_ENUM:
+		v->e.id = a->v.i.low;
+		v->e.name = (const char *)_pbcM_ip_query(f->type_name.e->id , a->v.i.low);
+		break;
+	case PTYPE_INT64:
+	case PTYPE_UINT64:
+	case PTYPE_INT32:
+	case PTYPE_UINT32:
+	case PTYPE_FIXED32:
+	case PTYPE_FIXED64:
+	case PTYPE_SFIXED32:
+	case PTYPE_SFIXED64:
+	case PTYPE_BOOL:
+		v->integer = a->v.i;
+		break;
+	case PTYPE_SINT32: 
+		v->integer = a->v.i;
+		_pbcV_dezigzag32(&(v->integer));
+		break;
+	case PTYPE_SINT64:
+		v->integer = a->v.i;
+		_pbcV_dezigzag64(&(v->integer));
+		break;
+	case PTYPE_STRING:
+		CHECK_LEND(a, );
+		read_string_var(h,v,a,f,buffer);
+		break;
+	case PTYPE_BYTES:
+		CHECK_LEND(a, );
+		v->s.str = (const char *)(buffer + a->v.s.start);
+		v->s.len = a->v.s.end - a->v.s.start;
+		break;
+	case PTYPE_MESSAGE: {
+		CHECK_LEND(a, );
+		struct pbc_rmessage message;
+		_pbc_rmessage_new(&message, f->type_name.m , 
+			buffer + a->v.s.start , 
+			a->v.s.end - a->v.s.start,h);
+		if (message.msg == NULL) {
+			return;
+		}
+		v->p[0] = message.msg;
+		v->p[1] = message.index;
+		break;
+	}
+	default:
+		return;
+	}
+
+	_pbcA_push(array,v);
+}
+
+static void
+_pbc_rmessage_new(struct pbc_rmessage * ret , struct _message * type , void *buffer, int size , struct heap *h) {
+	if (size == 0) {
+		ret->msg = type;
+		ret->index = _pbcM_sp_new(0 , h);
+		ret->heap = h;
+		return;
+	}
+	pbc_ctx _ctx;
+	int count = _pbcC_open(_ctx,buffer,size);
+	if (count <= 0) {
+		type->env->lasterror = "rmessage decode context error";
+		memset(ret , 0, sizeof(*ret));
+		return;
+	}
+	struct context * ctx = (struct context *)_ctx;
+
+	ret->msg = type;
+	ret->index = _pbcM_sp_new(count, h);
+	ret->heap = h;
+
+	int i;
+
+	for (i=0;i<ctx->number;i++) {
+		int id = ctx->a[i].wire_id >> 3;
+		struct _field * f = (struct _field *)_pbcM_ip_query(type->id , id);
+		if (f) {
+			if (f->label == LABEL_REPEATED || f->label == LABEL_PACKED) {
+				struct value * v;
+				void ** vv = _pbcM_sp_query_insert(ret->index, f->name);
+				if (*vv == NULL) {
+					v = (struct value *)_pbcH_alloc(h, SIZE_ARRAY);
+					v->type = f;
+					_pbcA_open_heap(v->v.array,ret->heap);
+					*vv = v;
+				} else {
+					v= (struct value *)*vv;
+				}
+				if (f->label == LABEL_PACKED) {
+					push_value_packed(type, v->v.array , f , &(ctx->a[i]), (uint8_t *)buffer);
+					if (pbc_array_size(v->v.array) == 0) {
+						type->env->lasterror = "rmessage decode packed data error";
+						*vv = NULL;
+					}
+				} else {
+					push_value_array(h,v->v.array , f, &(ctx->a[i]), (uint8_t *)buffer);
+					if (pbc_array_size(v->v.array) == 0) {
+						type->env->lasterror = "rmessage decode repeated data error";
+						*vv = NULL;
+					}
+				}
+			} else {
+				struct value * v = read_value(h, f, &(ctx->a[i]), (uint8_t *)buffer);
+				if (v) {
+					_pbcM_sp_insert(ret->index, f->name, v);
+				} else {
+					type->env->lasterror = "rmessage decode data error";
+				}
+			}
+		}
+	}
+
+	_pbcC_close(_ctx);
+}
+
+struct pbc_rmessage * 
+pbc_rmessage_new(struct pbc_env * env, const char * type_name ,  struct pbc_slice * slice) {
+	struct _message * msg = _pbcP_get_message(env, type_name);
+	if (msg == NULL) {
+		env->lasterror = "Proto not found";
+		return NULL;
+	}
+	struct pbc_rmessage temp;
+	struct heap * h = _pbcH_new(slice->len);
+	_pbc_rmessage_new(&temp, msg , slice->buffer, slice->len , h);
+	if (temp.msg == NULL) {
+		_pbcH_delete(h);
+		return NULL;
+	}
+
+	struct pbc_rmessage *m = (struct pbc_rmessage *)_pbcH_alloc(temp.heap, sizeof(*m));
+	*m = temp;
+	return m;
+}
+
+void 
+pbc_rmessage_delete(struct pbc_rmessage * m) {
+	if (m) {
+		_pbcH_delete(m->heap);
+	}
+}
+
+const char * 
+pbc_rmessage_string(struct pbc_rmessage * m , const char *key , int index, int *sz) {
+	struct value * v = (struct value *)_pbcM_sp_query(m->index,key);
+	int type = 0;
+	pbc_var var;
+	if (v == NULL) {
+		type = _pbcP_message_default(m->msg, key, var);
+	} else {
+		if (v->type->label == LABEL_REPEATED || v->type->label == LABEL_PACKED) {
+			_pbcA_index(v->v.array, index, var);
+		} else {
+			var[0] = v->v.var[0];
+		}
+		type = v->type->type;
+	}
+
+	if (type == PTYPE_ENUM) {
+		if (sz) {
+			*sz = strlen(var->e.name);
+		}
+		return var->e.name;
+	}
+
+	if (sz) {
+		int len = var->s.len;
+		if (len<0) {
+			len = - len;
+		}
+		*sz = len;
+	}
+	return var->s.str;
+}
+
+uint32_t 
+pbc_rmessage_integer(struct pbc_rmessage *m , const char *key , int index, uint32_t *hi) {
+	struct value * v = (struct value *)_pbcM_sp_query(m->index,key);
+	pbc_var var;
+	int type = 0;
+	if (v == NULL) {
+		type = _pbcP_message_default(m->msg, key, var);
+	} else {
+		if (v->type->label == LABEL_REPEATED || v->type->label == LABEL_PACKED) {
+			_pbcA_index(v->v.array, index, var);
+		} else {
+			var[0] = v->v.var[0];
+		}
+		type = v->type->type;
+	}
+
+	if (type == PTYPE_ENUM) {
+		if (hi) {
+			*hi = 0;
+		}
+		return var->e.id;
+	}
+
+	if (hi) {
+		*hi = var->integer.hi;
+	}
+	return var->integer.low;
+}
+
+double 
+pbc_rmessage_real(struct pbc_rmessage * m, const char *key , int index) {
+	struct value * v = (struct value *)_pbcM_sp_query(m->index,key);
+	pbc_var var;
+	if (v == NULL) {
+		_pbcP_message_default(m->msg, key, var);
+	} else {
+		if (v->type->label == LABEL_REPEATED || v->type->label == LABEL_PACKED) {
+			_pbcA_index(v->v.array, index, var);
+		} else {
+			return v->v.var->real;
+		}
+	}
+	return var->real;
+}
+
+
+struct pbc_rmessage * 
+pbc_rmessage_message(struct pbc_rmessage * rm, const char *key, int index) {
+	struct value * v = (struct value *)_pbcM_sp_query(rm->index,key);
+	if (v == NULL) {
+		struct _field * f = (struct _field *)_pbcM_sp_query(rm->msg->name, key);
+		if (f == NULL) {
+			rm->msg->env->lasterror = "Invalid key for sub-message";
+			// invalid key
+			return NULL;
+		}
+		struct _message * m = f->type_name.m;
+
+		if (m->def == NULL) {
+			// m->def will be free at the end (pbc_delete).
+			m->def = (struct pbc_rmessage *)malloc(sizeof(struct pbc_rmessage));
+			m->def->msg = m;
+			m->def->index = NULL;
+		}
+		return m->def;
+	} else {
+		if (v->type->label == LABEL_REPEATED) {
+			return (struct pbc_rmessage *)_pbcA_index_p(v->v.array,index);
+		} else {
+			return &(v->v.message);
+		}
+	}
+}
+
+int 
+pbc_rmessage_size(struct pbc_rmessage *m, const char *key) {
+	struct value * v = (struct value *)_pbcM_sp_query(m->index,key);
+	if (v == NULL) {
+		return 0;
+	}
+	if (v->type->label == LABEL_REPEATED || v->type->label == LABEL_PACKED) {
+		return pbc_array_size(v->v.array);
+	} else {
+		return 1;
+	}
+}
diff --git a/cbits/stringpool.c b/cbits/stringpool.c
new file mode 100644
--- /dev/null
+++ b/cbits/stringpool.c
@@ -0,0 +1,60 @@
+#include "alloc.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define PAGE_SIZE 256
+
+struct _stringpool {
+	char * buffer;
+	size_t len;
+	struct _stringpool *next;
+};
+
+struct _stringpool * 
+_pbcS_new(void) {
+	struct _stringpool * ret = (struct _stringpool *)malloc(sizeof(struct _stringpool) + PAGE_SIZE);
+	ret->buffer = (char *)(ret + 1);
+	ret->len = 0;
+	ret->next = NULL;
+	return ret;
+}
+
+void 
+_pbcS_delete(struct _stringpool *pool) {
+	while(pool) {
+		struct _stringpool *next = pool->next;
+		free(pool);
+		pool = next;
+	}
+}
+
+const char *
+_pbcS_build(struct _stringpool *pool, const char * str , int sz) {
+	size_t s = sz + 1;
+	if (s < PAGE_SIZE - pool->len) {
+		char * ret = pool->buffer + pool->len;
+		memcpy(pool->buffer + pool->len, str, s);
+		pool->len += s;
+		return ret;
+	}
+	if (s > PAGE_SIZE) {
+		struct _stringpool * next = (struct _stringpool *)malloc(sizeof(struct _stringpool) + s);
+		next->buffer = (char *)(next + 1);
+		memcpy(next->buffer, str, s);
+		next->len = s;
+		next->next = pool->next;
+		pool->next = next;
+		return next->buffer;
+	}
+	struct _stringpool *next = (struct _stringpool *)malloc(sizeof(struct _stringpool) + PAGE_SIZE);
+	next->buffer = pool->buffer;
+	next->next = pool->next;
+	next->len = pool->len;
+
+	pool->next = next;
+	pool->buffer = (char *)(next + 1);
+	memcpy(pool->buffer, str, s);
+	pool->len = s;
+	return pool->buffer;
+}
diff --git a/cbits/stringpool.h b/cbits/stringpool.h
new file mode 100644
--- /dev/null
+++ b/cbits/stringpool.h
@@ -0,0 +1,10 @@
+#ifndef PROTOBUF_C_STRINGPOOL_H
+#define PROTOBUF_C_STRINGPOOL_H
+
+struct _stringpool;
+
+struct _stringpool * _pbcS_new(void);
+void _pbcS_delete(struct _stringpool *pool);
+const char * _pbcS_build(struct _stringpool *pool, const char * str , int sz);
+
+#endif
diff --git a/cbits/varint.c b/cbits/varint.c
new file mode 100644
--- /dev/null
+++ b/cbits/varint.c
@@ -0,0 +1,110 @@
+#include "varint.h"
+
+#include "pbc.h"
+
+#include <stdint.h>
+
+inline int
+_pbcV_encode32(uint32_t number, uint8_t buffer[10])
+{
+	if (number < 0x80) {
+		buffer[0] = (uint8_t) number ; 
+		return 1;
+	}
+	buffer[0] = (uint8_t) (number | 0x80 );
+	if (number < 0x4000) {
+		buffer[1] = (uint8_t) (number >> 7 );
+		return 2;
+	}
+	buffer[1] = (uint8_t) ((number >> 7) | 0x80 );
+	if (number < 0x200000) {
+		buffer[2] = (uint8_t) (number >> 14);
+		return 3;
+	}
+	buffer[2] = (uint8_t) ((number >> 14) | 0x80 );
+	if (number < 0x10000000) {
+		buffer[3] = (uint8_t) (number >> 21);
+		return 4;
+	}
+	buffer[3] = (uint8_t) ((number >> 21) | 0x80 );
+	buffer[4] = (uint8_t) (number >> 28);
+	return 5;
+}
+
+int
+_pbcV_encode(uint64_t number, uint8_t buffer[10]) 
+{
+	if ((number & 0xffffffff) == number) {
+		return _pbcV_encode32((uint32_t)number , buffer);
+	}
+	int i = 0;
+	do {
+		buffer[i] = (uint8_t)(number | 0x80);
+		number >>= 7;
+		++i;
+	} while (number >= 0x80);
+	buffer[i] = (uint8_t)number;
+	return i+1;
+}
+
+int
+_pbcV_decode(uint8_t buffer[10], struct longlong *result) {
+	if (!(buffer[0] & 0x80)) {
+		result->low = buffer[0];
+		result->hi = 0;
+		return 1;
+	}
+	uint32_t r = buffer[0] & 0x7f;
+	int i;
+	for (i=1;i<4;i++) {
+		r |= ((buffer[i]&0x7f) << (7*i));
+		if (!(buffer[i] & 0x80)) {
+			result->low = r;
+			result->hi = 0;
+			return i+1;
+		}
+	}
+	uint64_t lr = 0;
+	for (i=4;i<10;i++) {
+		lr |= ((uint64_t)(buffer[i] & 0x7f) << (7*(i-4)));
+		if (!(buffer[i] & 0x80)) {
+			result->hi = (uint32_t)(lr >> 4);
+			result->low = r | (((uint32_t)lr & 0xf) << 28);
+			return i+1;
+		}
+	}
+
+	result->low = 0;
+	result->hi = 0;
+	return 10;
+}
+
+int 
+_pbcV_zigzag32(int32_t n, uint8_t buffer[10])
+{
+	n = (n << 1) ^ (n >> 31);
+	return _pbcV_encode32(n,buffer);
+}
+
+int 
+_pbcV_zigzag(int64_t n, uint8_t buffer[10])
+{
+	n = (n << 1) ^ (n >> 63);
+	return _pbcV_encode(n,buffer);
+}
+
+void
+_pbcV_dezigzag64(struct longlong *r)
+{
+	uint32_t low = r->low;
+	r->low = ((low >> 1) | ((r->hi & 1) << 31)) ^ - (low & 1);
+	r->hi = (r->hi >> 1) ^ - (low & 1);
+}
+
+void
+_pbcV_dezigzag32(struct longlong *r)
+{
+	uint32_t low = r->low;
+	r->low = (low >> 1) ^ - (low & 1);
+	r->hi = -(low >> 31);
+}
diff --git a/cbits/varint.h b/cbits/varint.h
new file mode 100644
--- /dev/null
+++ b/cbits/varint.h
@@ -0,0 +1,20 @@
+#ifndef PROTOBUF_C_VARINT_H
+#define PROTOBUF_C_VARINT_H
+
+#include <stdint.h>
+
+struct longlong {
+	uint32_t low;
+	uint32_t hi;
+};
+
+int _pbcV_encode32(uint32_t number, uint8_t buffer[10]);
+int _pbcV_encode(uint64_t number, uint8_t buffer[10]);
+int _pbcV_zigzag32(int32_t number, uint8_t buffer[10]);
+int _pbcV_zigzag(int64_t number, uint8_t buffer[10]);
+
+int _pbcV_decode(uint8_t buffer[10], struct longlong *result);
+void _pbcV_dezigzag64(struct longlong *r);
+void _pbcV_dezigzag32(struct longlong *r);
+
+#endif
diff --git a/cbits/wmessage.c b/cbits/wmessage.c
new file mode 100644
--- /dev/null
+++ b/cbits/wmessage.c
@@ -0,0 +1,516 @@
+#include "pbc.h"
+#include "context.h"
+#include "alloc.h"
+#include "varint.h"
+#include "map.h"
+#include "proto.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#ifndef _MSC_VER
+#include <stdbool.h>
+#endif
+
+#define WMESSAGE_SIZE 64
+
+struct pbc_wmessage {
+	struct _message *type;
+	uint8_t * buffer;
+	uint8_t * ptr;
+	uint8_t * endptr;
+	pbc_array sub;
+	struct map_sp *packed;
+	struct heap * heap;
+};
+
+struct _packed {
+	int id;
+	int ptype;
+	pbc_array data;
+};
+
+static struct pbc_wmessage *
+_wmessage_new(struct heap *h, struct _message *msg) {
+	struct pbc_wmessage * m = (struct pbc_wmessage *)_pbcH_alloc(h, sizeof(*m));
+	m->type = msg;
+	m->buffer = (uint8_t *)_pbcH_alloc(h, WMESSAGE_SIZE);
+	m->ptr = m->buffer;
+	m->endptr = m->buffer + WMESSAGE_SIZE;
+	_pbcA_open_heap(m->sub, h);
+	m->packed = NULL;
+	m->heap = h;
+
+	return m;
+}
+
+struct pbc_wmessage * 
+pbc_wmessage_new(struct pbc_env * env, const char *type_name) {
+	struct _message * msg = _pbcP_get_message(env, type_name);
+	if (msg == NULL)
+		return NULL;
+	struct heap *h = _pbcH_new(0);
+	return _wmessage_new(h, msg);
+}
+
+void 
+pbc_wmessage_delete(struct pbc_wmessage *m) {
+	if (m) {
+		_pbcH_delete(m->heap);
+	}
+}
+
+static void
+_expand_message(struct pbc_wmessage *m, int sz) {
+	if (m->ptr + sz > m->endptr) {
+		int cap = m->endptr - m->buffer;
+		sz = m->ptr + sz - m->buffer;
+		do {
+			cap = cap * 2;
+		} while ( sz > cap ) ;
+		int old_size = m->ptr - m->buffer;
+		uint8_t * buffer = (uint8_t *)_pbcH_alloc(m->heap, cap);
+		memcpy(buffer, m->buffer, old_size);
+		m->ptr = buffer + (m->ptr - m->buffer);
+		m->endptr = buffer + cap;
+		m->buffer = buffer;
+	}
+}
+
+static struct _packed *
+_get_packed(struct pbc_wmessage *m , struct _field *f , const char *key) {
+	if (m->packed == NULL) {
+		m->packed = _pbcM_sp_new(4, m->heap);
+	}
+	void ** v = _pbcM_sp_query_insert(m->packed , key);
+	if (*v == NULL) {
+		*v = _pbcH_alloc(m->heap, sizeof(struct _packed));
+		struct _packed *p = (struct _packed *)*v;
+		p->id = f->id;
+		p->ptype = f->type;
+		_pbcA_open_heap(p->data, m->heap);
+		return p;
+	}
+	return (struct _packed *)*v;
+}
+
+static void
+_packed_integer(struct pbc_wmessage *m, struct _field *f, const char *key , uint32_t low, uint32_t hi) {
+	struct _packed * packed = _get_packed(m,f,key);
+	pbc_var var;
+	var->integer.low = low;
+	var->integer.hi = hi;
+	_pbcA_push(packed->data , var);
+}
+
+static void
+_packed_real(struct pbc_wmessage *m, struct _field *f, const char *key , double v) {
+	struct _packed * packed = _get_packed(m,f,key);
+	pbc_var var;
+	var->real = v;
+	_pbcA_push(packed->data , var);
+}
+
+static inline void
+int64_encode(uint32_t low, uint32_t hi , uint8_t * buffer) {
+	buffer[0] = (uint8_t)(low & 0xff);
+	buffer[1] = (uint8_t)(low >> 8 & 0xff);
+	buffer[2] = (uint8_t)(low >> 16 & 0xff);
+	buffer[3] = (uint8_t)(low >> 24 & 0xff);
+	buffer[4] = (uint8_t)(hi & 0xff);
+	buffer[5] = (uint8_t)(hi >> 8 & 0xff);
+	buffer[6] = (uint8_t)(hi >> 16 & 0xff);
+	buffer[7] = (uint8_t)(hi >> 24 & 0xff);
+}
+
+static inline void
+int32_encode(uint32_t low, uint8_t * buffer) {
+	buffer[0] = (uint8_t)(low & 0xff);
+	buffer[1] = (uint8_t)(low >> 8 & 0xff);
+	buffer[2] = (uint8_t)(low >> 16 & 0xff);
+	buffer[3] = (uint8_t)(low >> 24 & 0xff);
+}
+
+int 
+pbc_wmessage_integer(struct pbc_wmessage *m, const char *key, uint32_t low, uint32_t hi) {
+	struct _field * f = (struct _field *)_pbcM_sp_query(m->type->name,key);
+	if (f==NULL) {
+		// todo : error
+		m->type->env->lasterror = "wmessage_interger query key error";
+		return -1;
+	}
+	if (f->label == LABEL_PACKED) {
+		_packed_integer(m , f, key , low, hi);
+		return 0;		
+	}
+	if (f->label == LABEL_OPTIONAL) {
+		if (f->type == PTYPE_ENUM) {
+			if (low == f->default_v->e.id)
+				return 0;
+		} else {
+			if (low == f->default_v->integer.low &&
+				hi == f->default_v->integer.hi) {
+				return 0;
+			}
+		}
+	}
+	int id = f->id << 3;
+
+	_expand_message(m,20);
+	switch (f->type) {
+	case PTYPE_INT64:
+	case PTYPE_UINT64: 
+	case PTYPE_INT32:
+		id |= WT_VARINT;
+		m->ptr += _pbcV_encode32(id, m->ptr);
+		m->ptr += _pbcV_encode((uint64_t)low | (uint64_t)hi << 32 , m->ptr);
+		break;
+	case PTYPE_UINT32:
+	case PTYPE_ENUM:
+	case PTYPE_BOOL:
+		id |= WT_VARINT;
+		m->ptr += _pbcV_encode32(id, m->ptr);
+		m->ptr += _pbcV_encode32(low, m->ptr);
+		break;
+	case PTYPE_FIXED64:
+	case PTYPE_SFIXED64:
+		id |= WT_BIT64;
+		m->ptr += _pbcV_encode32(id, m->ptr);
+		int64_encode(low,hi,m->ptr);
+		m->ptr += 8;
+		break;
+	case PTYPE_FIXED32:
+	case PTYPE_SFIXED32:
+		id |= WT_BIT32;
+		m->ptr += _pbcV_encode32(id, m->ptr);
+		int32_encode(low,m->ptr);
+		m->ptr += 4;
+		break;
+	case PTYPE_SINT32:
+		id |= WT_VARINT;
+		m->ptr += _pbcV_encode32(id, m->ptr);
+		m->ptr += _pbcV_zigzag32(low, m->ptr);
+		break;
+	case PTYPE_SINT64:
+		id |= WT_VARINT;
+		m->ptr += _pbcV_encode32(id, m->ptr);
+		m->ptr += _pbcV_zigzag((uint64_t)low | (uint64_t)hi << 32 , m->ptr);
+		break;
+	}
+
+	return 0;
+}
+
+int
+pbc_wmessage_real(struct pbc_wmessage *m, const char *key, double v) {
+	struct _field * f = (struct _field *)_pbcM_sp_query(m->type->name,key);
+	if (f == NULL) {
+		// todo : error
+		m->type->env->lasterror = "wmessage_real query key error";
+		return -1;
+	}
+	if (f->label == LABEL_PACKED) {
+		_packed_real(m , f, key , v);
+		return 0;		
+	}
+
+	if (f->label == LABEL_OPTIONAL) {
+		if (v == f->default_v->real)
+			return 0;
+	}
+	int id = f->id << 3;
+	_expand_message(m,18);
+	switch (f->type) {
+	case PTYPE_FLOAT: {
+		id |= WT_BIT32;
+		m->ptr += _pbcV_encode32(id, m->ptr);
+		float_encode(v , m->ptr);
+		m->ptr += 4;
+		break;
+	}
+	case PTYPE_DOUBLE:
+		id |= WT_BIT64;
+		m->ptr += _pbcV_encode32(id, m->ptr);
+		double_encode(v , m->ptr);
+		m->ptr += 8;
+		break;
+	}
+
+	return 0;
+}
+
+int
+pbc_wmessage_string(struct pbc_wmessage *m, const char *key, const char * v, int len) {
+	struct _field * f = (struct _field *)_pbcM_sp_query(m->type->name,key);
+	if (f == NULL) {
+		// todo : error
+		m->type->env->lasterror = "wmessage_string query key error";
+		return -1;
+	}
+
+	bool varlen = false;
+
+	if (len <=0) {
+		varlen = true;
+		// -1 for add '\0'
+		len = strlen(v) - len;
+	}
+	if (f->label == LABEL_PACKED) {
+		if (f->type == PTYPE_ENUM) {
+			char * temp = (char *)alloca(len + 1);
+			if (!varlen || v[len] != '\0') {
+				memcpy(temp,v,len);
+				temp[len]='\0';
+				v = temp;
+			}
+			int enum_id = 0;
+			int err = _pbcM_si_query(f->type_name.e->name, v , &enum_id);
+			if (err) {
+				// todo : error , invalid enum
+				m->type->env->lasterror = "wmessage_string packed invalid enum";
+				free(temp);
+				return -1;
+			}
+			_packed_integer(m , f, key , enum_id , 0);
+		}
+		return 0;	
+	}
+
+	if (f->label == LABEL_OPTIONAL) {
+		if (f->type == PTYPE_ENUM) {
+			if (strncmp(v , f->default_v->e.name, len) == 0 && f->default_v->e.name[len] =='\0') {
+				return 0;
+			}
+		} else if (f->type == PTYPE_STRING) {
+			if (len == f->default_v->s.len &&
+				strcmp(v, f->default_v->s.str) == 0) {
+				return 0;
+			}
+		}
+	}
+	int id = f->id << 3;
+	_expand_message(m,20);
+	switch (f->type) {
+	case PTYPE_ENUM : {
+		char * temp = (char *)malloc(len+1);
+		if (!varlen || v[len] != '\0') {
+			memcpy(temp,v,len);
+			temp[len]='\0';
+			v = temp;
+		}
+		int enum_id = 0;
+		int err = _pbcM_si_query(f->type_name.e->name, v, &enum_id);
+		if (err) {
+			// todo : error , enum invalid
+			m->type->env->lasterror = "wmessage_string invalid enum";
+			free(temp);
+			return -1;
+		}
+		id |= WT_VARINT;
+		m->ptr += _pbcV_encode32(id, m->ptr);
+		m->ptr += _pbcV_encode32(enum_id, m->ptr);
+		break;
+	}
+	case PTYPE_STRING:
+	case PTYPE_BYTES:
+		id |= WT_LEND;
+		m->ptr += _pbcV_encode32(id, m->ptr);
+		m->ptr += _pbcV_encode32(len, m->ptr);
+		_expand_message(m,len);
+		memcpy(m->ptr , v , len);
+		m->ptr += len;
+		break;
+	}
+
+	return 0;
+}
+
+struct pbc_wmessage * 
+pbc_wmessage_message(struct pbc_wmessage *m, const char *key) {
+	struct _field * f = (struct _field *)_pbcM_sp_query(m->type->name,key);
+	if (f == NULL) {
+		// todo : error
+		m->type->env->lasterror = "wmessage_message query key error";
+		return NULL;
+	}
+	pbc_var var;
+	var->p[0] = _wmessage_new(m->heap, f->type_name.m);
+	var->p[1] = f;
+	_pbcA_push(m->sub , var);
+	return (struct pbc_wmessage *)var->p[0];
+}
+
+static void
+_pack_packed_64(struct _packed *p,struct pbc_wmessage *m) {
+	int n = pbc_array_size(p->data);
+	int len = n * 8;
+	int i;
+	pbc_var var;
+	_expand_message(m,10 + len);
+	m->ptr += _pbcV_encode32(len, m->ptr);
+	switch (p->ptype) {
+	case PTYPE_DOUBLE:
+		for (i=0;i<n;i++) {
+			_pbcA_index(p->data, i, var);
+			double_encode(var->real , m->ptr + i * 8);
+		}
+		break;
+	default:
+		for (i=0;i<n;i++) {
+			_pbcA_index(p->data, i, var);
+			int64_encode(var->integer.low , var->integer.hi, m->ptr + i * 8);
+		}
+		break;
+	}
+	m->ptr += len;
+}
+
+static void
+_pack_packed_32(struct _packed *p,struct pbc_wmessage *m) {
+	int n = pbc_array_size(p->data);
+	int len = n * 4;
+	int i;
+	pbc_var var;
+	_expand_message(m,10 + len);
+	m->ptr += _pbcV_encode32(len, m->ptr);
+	switch (p->ptype) {
+	case PTYPE_FLOAT:
+		for (i=0;i<n;i++) {
+			_pbcA_index(p->data, i, var);
+			float_encode(var->real , m->ptr + i * 8);
+		}
+		break;
+	default:
+		for (i=0;i<n;i++) {
+			_pbcA_index(p->data, i, var);
+			int32_encode(var->integer.low , m->ptr + i * 8);
+		}
+		break;
+	}
+	m->ptr += len;
+}
+
+static void
+_pack_packed_varint(struct _packed *p,struct pbc_wmessage *m) {
+	int n = pbc_array_size(p->data);
+
+	int offset = m->ptr - m->buffer;
+	int len = n * 2;
+	if (p->ptype == PTYPE_BOOL) {
+		len = n;
+	}
+	int i;
+	pbc_var var;
+	_expand_message(m,10 + len);
+	int len_len = _pbcV_encode32(len, m->ptr);
+	m->ptr += len_len;
+
+	switch (p->ptype) {
+	case PTYPE_INT64:
+	case PTYPE_UINT64:
+		for (i=0;i<n;i++) {
+			_pbcA_index(p->data, i, var);
+			_expand_message(m,10);
+			m->ptr += _pbcV_encode((uint64_t)var->integer.low | (uint64_t)var->integer.hi << 32 , m->ptr);
+		}
+		break;
+	case PTYPE_INT32:
+	case PTYPE_BOOL:
+	case PTYPE_UINT32:
+	case PTYPE_ENUM:
+		for (i=0;i<n;i++) {
+			_pbcA_index(p->data, i, var);
+			_expand_message(m,10);
+			m->ptr += _pbcV_encode32(var->integer.low , m->ptr);
+		}
+		break;
+	case PTYPE_SINT32:
+		for (i=0;i<n;i++) {
+			_pbcA_index(p->data, i, var);
+			_expand_message(m,10);
+			m->ptr += _pbcV_zigzag32(var->integer.low, m->ptr);
+		}
+		break;
+	case PTYPE_SINT64:
+		for (i=0;i<n;i++) {
+			_pbcA_index(p->data, i, var);
+			_expand_message(m,10);
+			m->ptr += _pbcV_zigzag((uint64_t)var->integer.low | (uint64_t)var->integer.hi << 32 , m->ptr);
+		}
+		break;
+	default:
+		// error
+		memset(m->ptr , 0 , n);
+		m->ptr += n;
+		m->type->env->lasterror = "wmessage type error when pack packed";
+		break;
+	}
+	int end_offset = m->ptr - m->buffer;
+	int end_len = end_offset - (offset + len_len);
+	if (end_len != len) {
+		uint8_t temp[10];
+		int end_len_len = _pbcV_encode32(end_len, temp);
+		if (end_len_len != len_len) {
+			_expand_message(m, end_len_len);
+			memmove(m->buffer + offset + end_len_len , 
+				m->buffer + offset + len_len , 
+				end_len);
+			m->ptr +=  end_len_len - len_len;
+		}
+		memcpy(m->buffer + offset , temp, end_len_len);
+	}
+}
+
+static void
+_pack_packed(void *p, void *ud) {
+	struct _packed *packed = (struct _packed *)p;
+	struct pbc_wmessage * m = (struct pbc_wmessage *)ud;
+	int id = packed->id << 3 | WT_LEND;
+	_expand_message(m,10);
+	m->ptr += _pbcV_encode32(id, m->ptr);
+	switch(packed->ptype) {
+	case PTYPE_DOUBLE:
+	case PTYPE_FIXED64:
+	case PTYPE_SFIXED64:
+		_pack_packed_64(packed,m);
+		break;
+	case PTYPE_FLOAT:
+	case PTYPE_FIXED32:
+	case PTYPE_SFIXED32:
+		_pack_packed_32(packed,m);
+		break;
+	default:
+		_pack_packed_varint(packed,m);
+		break;
+	}
+}
+
+void * 
+pbc_wmessage_buffer(struct pbc_wmessage *m, struct pbc_slice *slice) {
+	if (m->packed) {
+		_pbcM_sp_foreach_ud(m->packed , _pack_packed, m);
+	}
+	int i;
+	int n = pbc_array_size(m->sub);
+	for (i=0;i<n;i++) {
+		pbc_var var;
+		_pbcA_index(m->sub, i , var);
+		struct pbc_slice s;
+		pbc_wmessage_buffer((struct pbc_wmessage *)var->p[0] , &s);
+		if (s.buffer) {
+			struct _field * f = (struct _field *)var->p[1];
+			int id = f->id << 3 | WT_LEND;
+			_expand_message(m,20+s.len);
+			m->ptr += _pbcV_encode32(id, m->ptr);
+			m->ptr += _pbcV_encode32(s.len, m->ptr);
+			memcpy(m->ptr, s.buffer, s.len);
+			m->ptr += s.len;
+		}
+	}
+	slice->buffer = m->buffer;
+	slice->len = m->ptr - m->buffer;
+
+	return m->buffer;
+}
+
diff --git a/pbc4hs.cabal b/pbc4hs.cabal
new file mode 100644
--- /dev/null
+++ b/pbc4hs.cabal
@@ -0,0 +1,47 @@
+name:                pbc4hs
+version:             0.1.0.0
+synopsis:            pbc for HsLua
+description:         pbc wrapper for HsLua
+license:             BSD3
+license-file:        LICENSE
+author:              david feng
+maintainer:          davidxifeng@gmail.com
+category:            System
+build-type:          Simple
+cabal-version:       >=1.10
+
+extra-source-files:  cbits/alloc.h
+                     cbits/array.h
+                     cbits/bootstrap.h
+                     cbits/context.h
+                     cbits/descriptor.pbc.h
+                     cbits/map.h
+                     cbits/pattern.h
+                     cbits/pbc.h
+                     cbits/proto.h
+                     cbits/stringpool.h
+                     cbits/varint.h
+
+library
+  exposed-modules:     Scripting.Lua.Pbc
+  other-extensions:    ForeignFunctionInterface
+  build-depends:       base >=4.6 && <4.7, hslua >=0.3 && <0.4
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+
+  include-dirs:        cbits
+  c-sources:           cbits/alloc.c
+                       cbits/array.c
+                       cbits/bootstrap.c
+                       cbits/context.c
+                       cbits/decode.c
+                       cbits/map.c
+                       cbits/pattern.c
+                       cbits/proto.c
+                       cbits/register.c
+                       cbits/rmessage.c
+                       cbits/stringpool.c
+                       cbits/varint.c
+                       cbits/wmessage.c
+                       cbits/pbc-lua.c
+  cc-options:          -Wall
diff --git a/src/Scripting/Lua/Pbc.hs b/src/Scripting/Lua/Pbc.hs
new file mode 100644
--- /dev/null
+++ b/src/Scripting/Lua/Pbc.hs
@@ -0,0 +1,16 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- | pbc library wrapper, useful when sometimes require from dylib doesn't
+-- work well
+module Scripting.Lua.Pbc ( -- ** functions
+                           openPbc
+                         ) where
+
+import Scripting.Lua
+
+foreign import ccall "luaopen_protobuf_c" c_luaopen_pbc :: LuaState -> IO ()
+
+-- | open pbc Lua library
+openPbc :: LuaState -> IO ()
+openPbc = c_luaopen_pbc
+
