diff --git a/pbc4hs.cabal b/pbc4hs.cabal
--- a/pbc4hs.cabal
+++ b/pbc4hs.cabal
@@ -1,5 +1,5 @@
 name:                pbc4hs
-version:             0.1.0.0
+version:             0.1.1.0
 synopsis:            pbc for HsLua
 description:         pbc wrapper for HsLua
 license:             BSD3
@@ -9,6 +9,7 @@
 category:            System
 build-type:          Simple
 cabal-version:       >=1.10
+homepage:            https://github.com/DavidFeng/pbc4hs
 
 extra-source-files:  cbits/alloc.h
                      cbits/array.h
@@ -22,10 +23,15 @@
                      cbits/stringpool.h
                      cbits/varint.h
 
+source-repository head
+  type:                 git
+  location:             https://github.com/osa1/hslua.git
+
 library
   exposed-modules:     Scripting.Lua.Pbc
   other-extensions:    ForeignFunctionInterface
   build-depends:       base >=4.6 && <4.7, hslua >=0.3 && <0.4
+                       , string-qq >= 0.0.2
   hs-source-dirs:      src
   default-language:    Haskell2010
 
diff --git a/src/Scripting/Lua/Pbc.hs b/src/Scripting/Lua/Pbc.hs
--- a/src/Scripting/Lua/Pbc.hs
+++ b/src/Scripting/Lua/Pbc.hs
@@ -1,16 +1,584 @@
 {-# LANGUAGE ForeignFunctionInterface #-}
+{-# LANGUAGE QuasiQuotes              #-}
 
 -- | pbc library wrapper, useful when sometimes require from dylib doesn't
 -- work well
 module Scripting.Lua.Pbc ( -- ** functions
                            openPbc
+                         , openLuaPbc
                          ) where
 
 import Scripting.Lua
+import Data.String.QQ
 
 foreign import ccall "luaopen_protobuf_c" c_luaopen_pbc :: LuaState -> IO ()
 
--- | open pbc Lua library
+-- | open pbc Lua C binding library
 openPbc :: LuaState -> IO ()
 openPbc = c_luaopen_pbc
 
+-- | open pbc Lua library
+openLuaPbc :: LuaState -> IO ()
+openLuaPbc l = do
+    c_luaopen_pbc l
+    loadstring l protobuf_lua "protobuf.lua"
+    call l 0 0
+
+protobuf_lua :: String
+protobuf_lua = [s|
+local c = require "protobuf.c"
+
+local setmetatable = setmetatable
+local type = type
+local table = table
+local assert = assert
+local pairs = pairs
+local ipairs = ipairs
+local string = string
+local print = print
+local io = io
+local tinsert = table.insert
+local rawget = rawget
+
+module "protobuf"
+
+local _pattern_cache = {}
+
+-- skynet clear
+local P = c._env_new()
+local GC = c._gc(P)
+
+function lasterror()
+    return c._last_error(P)
+end
+
+local decode_type_cache = {}
+local _R_meta = {}
+
+function _R_meta:__index(key)
+    local v = decode_type_cache[self._CType][key](self, key)
+    self[key] = v
+    return v
+end
+
+local _reader = {}
+
+function _reader:int(key)
+    return c._rmessage_integer(self._CObj , key , 0)
+end
+
+function _reader:real(key)
+    return c._rmessage_real(self._CObj , key , 0)
+end
+
+function _reader:string(key)
+    return c._rmessage_string(self._CObj , key , 0)
+end
+
+function _reader:bool(key)
+    return c._rmessage_integer(self._CObj , key , 0) ~= 0
+end
+
+function _reader:message(key, message_type)
+    local rmessage = c._rmessage_message(self._CObj , key , 0)
+    if rmessage then
+        local v = {
+            _CObj = rmessage,
+            _CType = message_type,
+            _Parent = self,
+        }
+        return setmetatable( v , _R_meta )
+    end
+end
+
+function _reader:int32(key)
+    return c._rmessage_int32(self._CObj , key , 0)
+end
+
+function _reader:int64(key)
+    return c._rmessage_int64(self._CObj , key , 0)
+end
+
+function _reader:int52(key)
+    return c._rmessage_int52(self._CObj , key , 0)
+end
+
+function _reader:uint52(key)
+    return c._rmessage_uint52(self._CObj , key , 0)
+end
+
+function _reader:int_repeated(key)
+    local cobj = self._CObj
+    local n = c._rmessage_size(cobj , key)
+    local ret = {}
+    for i=0,n-1 do
+        tinsert(ret,  c._rmessage_integer(cobj , key , i))
+    end
+    return ret
+end
+
+function _reader:real_repeated(key)
+    local cobj = self._CObj
+    local n = c._rmessage_size(cobj , key)
+    local ret = {}
+    for i=0,n-1 do
+        tinsert(ret,  c._rmessage_real(cobj , key , i))
+    end
+    return ret
+end
+
+function _reader:string_repeated(key)
+    local cobj = self._CObj
+    local n = c._rmessage_size(cobj , key)
+    local ret = {}
+    for i=0,n-1 do
+        tinsert(ret,  c._rmessage_string(cobj , key , i))
+    end
+    return ret
+end
+
+function _reader:bool_repeated(key)
+    local cobj = self._CObj
+    local n = c._rmessage_size(cobj , key)
+    local ret = {}
+    for i=0,n-1 do
+        tinsert(ret,  c._rmessage_integer(cobj , key , i) ~= 0)
+    end
+    return ret
+end
+
+function _reader:message_repeated(key, message_type)
+    local cobj = self._CObj
+    local n = c._rmessage_size(cobj , key)
+    local ret = {}
+    for i=0,n-1 do
+        local m = {
+            _CObj = c._rmessage_message(cobj , key , i),
+            _CType = message_type,
+            _Parent = self,
+        }
+        tinsert(ret, setmetatable( m , _R_meta ))
+    end
+    return ret
+end
+
+function _reader:int32_repeated(key)
+    local cobj = self._CObj
+    local n = c._rmessage_size(cobj , key)
+    local ret = {}
+    for i=0,n-1 do
+        tinsert(ret,  c._rmessage_int32(cobj , key , i))
+    end
+    return ret
+end
+
+function _reader:int64_repeated(key)
+    local cobj = self._CObj
+    local n = c._rmessage_size(cobj , key)
+    local ret = {}
+    for i=0,n-1 do
+        tinsert(ret,  c._rmessage_int64(cobj , key , i))
+    end
+    return ret
+end
+
+function _reader:int52_repeated(key)
+    local cobj = self._CObj
+    local n = c._rmessage_size(cobj , key)
+    local ret = {}
+    for i=0,n-1 do
+        tinsert(ret,  c._rmessage_int52(cobj , key , i))
+    end
+    return ret
+end
+
+function _reader:uint52_repeated(key)
+    local cobj = self._CObj
+    local n = c._rmessage_size(cobj , key)
+    local ret = {}
+    for i=0,n-1 do
+        tinsert(ret,  c._rmessage_uint52(cobj , key , i))
+    end
+    return ret
+end
+
+_reader[1] = function(msg) return _reader.int end
+_reader[2] = function(msg) return _reader.real end
+_reader[3] = function(msg) return _reader.bool end
+_reader[4] = function(msg) return _reader.string end
+_reader[5] = function(msg) return _reader.string end
+_reader[6] = function(msg)
+    local message = _reader.message
+    return  function(self,key)
+            return message(self, key, msg)
+        end
+end
+_reader[7] = function(msg) return _reader.int64 end
+_reader[8] = function(msg) return _reader.int32 end
+_reader[9] = _reader[5]
+_reader[10] = function(msg) return _reader.int52 end
+_reader[11] = function(msg) return _reader.uint52 end
+
+_reader[128+1] = function(msg) return _reader.int_repeated end
+_reader[128+2] = function(msg) return _reader.real_repeated end
+_reader[128+3] = function(msg) return _reader.bool_repeated end
+_reader[128+4] = function(msg) return _reader.string_repeated end
+_reader[128+5] = function(msg) return _reader.string_repeated end
+_reader[128+6] = function(msg)
+    local message = _reader.message_repeated
+    return  function(self,key)
+            return message(self, key, msg)
+        end
+end
+_reader[128+7] = function(msg) return _reader.int64_repeated end
+_reader[128+8] = function(msg) return _reader.int32_repeated end
+_reader[128+9] = _reader[128+5]
+_reader[128+10] = function(msg) return _reader.int52_repeated end
+_reader[128+11] = function(msg) return _reader.uint52_repeated end
+
+local _decode_type_meta = {}
+
+function _decode_type_meta:__index(key)
+    local t, msg = c._env_type(P, self._CType, key)
+    local func = assert(_reader[t],key)(msg)
+    self[key] = func
+    return func
+end
+
+setmetatable(decode_type_cache , {
+    __index = function(self, key)
+        local v = setmetatable({ _CType = key } , _decode_type_meta)
+        self[key] = v
+        return v
+    end
+})
+
+local function decode_message( message , buffer, length)
+    local rmessage = c._rmessage_new(P, message, buffer, length)
+    if rmessage then
+        local self = {
+            _CObj = rmessage,
+            _CType = message,
+        }
+        c._add_rmessage(GC,rmessage)
+        return setmetatable( self , _R_meta )
+    end
+end
+
+----------- encode ----------------
+
+local encode_type_cache = {}
+
+local function encode_message(CObj, message_type, t)
+    local type = encode_type_cache[message_type]
+    for k,v in pairs(t) do
+        local func = type[k]
+        func(CObj, k , v)
+    end
+end
+
+local _writer = {
+    int = c._wmessage_integer,
+    real = c._wmessage_real,
+    enum = c._wmessage_string,
+    string = c._wmessage_string,
+    int64 = c._wmessage_int64,
+    int32 = c._wmessage_int32,
+    int52 = c._wmessage_int52,
+    uint52 = c._wmessage_uint52,
+}
+
+function _writer:bool(k,v)
+    c._wmessage_integer(self, k, v and 1 or 0)
+end
+
+function _writer:message(k, v , message_type)
+    local submessage = c._wmessage_message(self, k)
+    encode_message(submessage, message_type, v)
+end
+
+function _writer:int_repeated(k,v)
+    for _,v in ipairs(v) do
+        c._wmessage_integer(self,k,v)
+    end
+end
+
+function _writer:real_repeated(k,v)
+    for _,v in ipairs(v) do
+        c._wmessage_real(self,k,v)
+    end
+end
+
+function _writer:bool_repeated(k,v)
+    for _,v in ipairs(v) do
+        c._wmessage_integer(self, k, v and 1 or 0)
+    end
+end
+
+function _writer:string_repeated(k,v)
+    for _,v in ipairs(v) do
+        c._wmessage_string(self,k,v)
+    end
+end
+
+function _writer:message_repeated(k,v, message_type)
+    for _,v in ipairs(v) do
+        local submessage = c._wmessage_message(self, k)
+        encode_message(submessage, message_type, v)
+    end
+end
+
+function _writer:int32_repeated(k,v)
+    for _,v in ipairs(v) do
+        c._wmessage_int32(self,k,v)
+    end
+end
+
+function _writer:int64_repeated(k,v)
+    for _,v in ipairs(v) do
+        c._wmessage_int64(self,k,v)
+    end
+end
+
+function _writer:int52_repeated(k,v)
+    for _,v in ipairs(v) do
+        c._wmessage_int52(self,k,v)
+    end
+end
+
+function _writer:uint52_repeated(k,v)
+    for _,v in ipairs(v) do
+        c._wmessage_uint52(self,k,v)
+    end
+end
+
+_writer[1] = function(msg) return _writer.int end
+_writer[2] = function(msg) return _writer.real end
+_writer[3] = function(msg) return _writer.bool end
+_writer[4] = function(msg) return _writer.string end
+_writer[5] = function(msg) return _writer.string end
+_writer[6] = function(msg)
+    local message = _writer.message
+    return  function(self,key , v)
+            return message(self, key, v, msg)
+        end
+end
+_writer[7] = function(msg) return _writer.int64 end
+_writer[8] = function(msg) return _writer.int32 end
+_writer[9] = _writer[5]
+_writer[10] = function(msg) return _writer.int52 end
+_writer[11] = function(msg) return _writer.uint52 end
+
+_writer[128+1] = function(msg) return _writer.int_repeated end
+_writer[128+2] = function(msg) return _writer.real_repeated end
+_writer[128+3] = function(msg) return _writer.bool_repeated end
+_writer[128+4] = function(msg) return _writer.string_repeated end
+_writer[128+5] = function(msg) return _writer.string_repeated end
+_writer[128+6] = function(msg)
+    local message = _writer.message_repeated
+    return  function(self,key, v)
+            return message(self, key, v, msg)
+        end
+end
+_writer[128+7] = function(msg) return _writer.int64_repeated end
+_writer[128+8] = function(msg) return _writer.int32_repeated end
+_writer[128+9] = _writer[128+5]
+_writer[128+10] = function(msg) return _writer.int52_repeated end
+_writer[128+11] = function(msg) return _writer.uint52_repeated end
+
+local _encode_type_meta = {}
+
+function _encode_type_meta:__index(key)
+    local t, msg = c._env_type(P, self._CType, key)
+    local func = assert(_writer[t],key)(msg)
+    self[key] = func
+    return func
+end
+
+setmetatable(encode_type_cache , {
+    __index = function(self, key)
+        local v = setmetatable({ _CType = key } , _encode_type_meta)
+        self[key] = v
+        return v
+    end
+})
+
+function encode( message, t , func , ...)
+    local encoder = c._wmessage_new(P, message)
+    assert(encoder ,  message)
+    encode_message(encoder, message, t)
+    if func then
+        local buffer, len = c._wmessage_buffer(encoder)
+        local ret = func(buffer, len, ...)
+        c._wmessage_delete(encoder)
+        return ret
+    else
+        local s = c._wmessage_buffer_string(encoder)
+        c._wmessage_delete(encoder)
+        return s
+    end
+end
+
+--------- unpack ----------
+
+local _pattern_type = {
+    [1] = {"%d","i"},
+    [2] = {"%F","r"},
+    [3] = {"%d","b"},
+    [4] = {"%d","i"},
+    [5] = {"%s","s"},
+    [6] = {"%s","m"},
+    [7] = {"%D","x"},
+    [8] = {"%d","p"},
+    [10] =  {"%D","d"},
+    [11] =  {"%D","u"},
+    [128+1] = {"%a","I"},
+    [128+2] = {"%a","R"},
+    [128+3] = {"%a","B"},
+    [128+4] = {"%a","I"},
+    [128+5] = {"%a","S"},
+    [128+6] = {"%a","M"},
+    [128+7] = {"%a","X"},
+    [128+8] = {"%a","P"},
+    [128+10] = {"%a", "D" },
+    [128+11] = {"%a", "U" },
+}
+
+_pattern_type[9] = _pattern_type[5]
+_pattern_type[128+9] = _pattern_type[128+5]
+
+
+local function _pattern_create(pattern)
+    local iter = string.gmatch(pattern,"[^ ]+")
+    local message = iter()
+    local cpat = {}
+    local lua = {}
+    for v in iter do
+        local tidx = c._env_type(P, message, v)
+        local t = _pattern_type[tidx]
+        assert(t,tidx)
+        tinsert(cpat,v .. " " .. t[1])
+        tinsert(lua,t[2])
+    end
+    local cobj = c._pattern_new(P, message , "@" .. table.concat(cpat," "))
+    if cobj == nil then
+        return
+    end
+    c._add_pattern(GC, cobj)
+    local pat = {
+        CObj = cobj,
+        format = table.concat(lua),
+        size = 0
+    }
+    pat.size = c._pattern_size(pat.format)
+
+    return pat
+end
+
+setmetatable(_pattern_cache, {
+    __index = function(t, key)
+        local v = _pattern_create(key)
+        t[key] = v
+        return v
+    end
+})
+
+function unpack(pattern, buffer, length)
+    local pat = _pattern_cache[pattern]
+    return c._pattern_unpack(pat.CObj , pat.format, pat.size, buffer, length)
+end
+
+function pack(pattern, ...)
+    local pat = _pattern_cache[pattern]
+    return c._pattern_pack(pat.CObj, pat.format, pat.size , ...)
+end
+
+function check(typename , field)
+    if field == nil then
+        return c._env_type(P,typename)
+    else
+        return c._env_type(P,typename,field) ~=0
+    end
+end
+
+--------------
+
+local default_cache = {}
+
+-- todo : clear default_cache, v._CObj
+
+local function default_table(typename)
+    local v = default_cache[typename]
+    if v then
+        return v
+    end
+
+    v = { __index = assert(decode_message(typename , "")) }
+
+    default_cache[typename]  = v
+    return v
+end
+
+local decode_message_mt = {}
+
+local function decode_message_cb(typename, buffer)
+    return setmetatable ( { typename, buffer } , decode_message_mt)
+end
+
+function decode(typename, buffer, length)
+    local ret = {}
+    local ok = c._decode(P, decode_message_cb , ret , typename, buffer, length)
+    if ok then
+        return setmetatable(ret , default_table(typename))
+    else
+        return false , c._last_error(P)
+    end
+end
+
+local function expand(tbl)
+    local typename = rawget(tbl , 1)
+    local buffer = rawget(tbl , 2)
+    tbl[1] , tbl[2] = nil , nil
+    assert(c._decode(P, decode_message_cb , tbl , typename, buffer), typename)
+    setmetatable(tbl , default_table(typename))
+end
+
+function decode_message_mt.__index(tbl, key)
+    expand(tbl)
+    return tbl[key]
+end
+
+function decode_message_mt.__pairs(tbl)
+    expand(tbl)
+    return pairs(tbl)
+end
+
+local function set_default(typename, tbl)
+    for k,v in pairs(tbl) do
+        if type(v) == "table" then
+            local t, msg = c._env_type(P, typename, k)
+            if t == 6 then
+                set_default(msg, v)
+            elseif t == 128+6 then
+                for _,v in ipairs(v) do
+                    set_default(msg, v)
+                end
+            end
+        end
+    end
+    return setmetatable(tbl , default_table(typename))
+end
+
+function register(buffer)
+    c._env_register(P, buffer)
+end
+
+function register_file(filename)
+    local f = assert(io.open(filename , "rb"))
+    local buffer = f:read "*a"
+    c._env_register(P, buffer)
+    f:close()
+end
+
+default=set_default
+|]
