475 lines
12 KiB
C
475 lines
12 KiB
C
/*
|
|
* =====================================================================================
|
|
*
|
|
* Filename: pb.c
|
|
*
|
|
* Description: protoc-gen-lua
|
|
* Google's Protocol Buffers project, ported to lua.
|
|
* https://code.google.com/p/protoc-gen-lua/
|
|
*
|
|
* Copyright (c) 2010 , 林卓毅 (Zhuoyi Lin) netsnail@gmail.com
|
|
* All rights reserved.
|
|
*
|
|
* Use, modification and distribution are subject to the "New BSD License"
|
|
* as listed at <url: http://www.opensource.org/licenses/bsd-license.php >.
|
|
*
|
|
* Created: 2010年08月02日 18时04分21秒
|
|
*
|
|
* Company: NetEase
|
|
*
|
|
* =====================================================================================
|
|
*/
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include <lua.h>
|
|
#include <lualib.h>
|
|
#include <lauxlib.h>
|
|
|
|
#ifdef _ALLBSD_SOURCE
|
|
#include <machine/endian.h>
|
|
#else
|
|
#include <endian.h>
|
|
#endif
|
|
|
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
#define IS_LITTLE_ENDIAN
|
|
#endif
|
|
|
|
#define IOSTRING_META "protobuf.IOString"
|
|
|
|
#define checkiostring(L) \
|
|
(IOString*) luaL_checkudata(L, 1, IOSTRING_META)
|
|
|
|
#define IOSTRING_BUF_LEN 65535
|
|
|
|
typedef struct{
|
|
size_t size;
|
|
char buf[IOSTRING_BUF_LEN];
|
|
} IOString;
|
|
|
|
static void pack_varint(luaL_Buffer *b, uint64_t value)
|
|
{
|
|
if (value >= 0x80)
|
|
{
|
|
luaL_addchar(b, value | 0x80);
|
|
value >>= 7;
|
|
if (value >= 0x80)
|
|
{
|
|
luaL_addchar(b, value | 0x80);
|
|
value >>= 7;
|
|
if (value >= 0x80)
|
|
{
|
|
luaL_addchar(b, value | 0x80);
|
|
value >>= 7;
|
|
if (value >= 0x80)
|
|
{
|
|
luaL_addchar(b, value | 0x80);
|
|
value >>= 7;
|
|
if (value >= 0x80)
|
|
{
|
|
luaL_addchar(b, value | 0x80);
|
|
value >>= 7;
|
|
if (value >= 0x80)
|
|
{
|
|
luaL_addchar(b, value | 0x80);
|
|
value >>= 7;
|
|
if (value >= 0x80)
|
|
{
|
|
luaL_addchar(b, value | 0x80);
|
|
value >>= 7;
|
|
if (value >= 0x80)
|
|
{
|
|
luaL_addchar(b, value | 0x80);
|
|
value >>= 7;
|
|
if (value >= 0x80)
|
|
{
|
|
luaL_addchar(b, value | 0x80);
|
|
value >>= 7;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
luaL_addchar(b, value);
|
|
}
|
|
|
|
static int varint_encoder(lua_State *L)
|
|
{
|
|
lua_Number l_value = luaL_checknumber(L, 2);
|
|
uint64_t value = (uint64_t)l_value;
|
|
|
|
luaL_Buffer b;
|
|
luaL_buffinit(L, &b);
|
|
|
|
pack_varint(&b, value);
|
|
|
|
lua_settop(L, 1);
|
|
luaL_pushresult(&b);
|
|
lua_call(L, 1, 0);
|
|
return 0;
|
|
}
|
|
|
|
static int signed_varint_encoder(lua_State *L)
|
|
{
|
|
lua_Number l_value = luaL_checknumber(L, 2);
|
|
int64_t value = (int64_t)l_value;
|
|
|
|
luaL_Buffer b;
|
|
luaL_buffinit(L, &b);
|
|
|
|
if (value < 0)
|
|
{
|
|
pack_varint(&b, *(uint64_t*)&value);
|
|
}else{
|
|
pack_varint(&b, value);
|
|
}
|
|
|
|
lua_settop(L, 1);
|
|
luaL_pushresult(&b);
|
|
lua_call(L, 1, 0);
|
|
return 0;
|
|
}
|
|
|
|
static int pack_fixed32(lua_State *L, uint8_t* value){
|
|
#ifdef IS_LITTLE_ENDIAN
|
|
lua_pushlstring(L, (char*)value, 4);
|
|
#else
|
|
uint32_t v = htole32(*(uint32_t*)value);
|
|
lua_pushlstring(L, (char*)&v, 4);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int pack_fixed64(lua_State *L, uint8_t* value){
|
|
#ifdef IS_LITTLE_ENDIAN
|
|
lua_pushlstring(L, (char*)value, 8);
|
|
#else
|
|
uint64_t v = htole64(*(uint64_t*)value);
|
|
lua_pushlstring(L, (char*)&v, 8);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int struct_pack(lua_State *L)
|
|
{
|
|
uint8_t format = luaL_checkinteger(L, 2);
|
|
lua_Number value = luaL_checknumber(L, 3);
|
|
lua_settop(L, 1);
|
|
|
|
switch(format){
|
|
case 'i':
|
|
{
|
|
int32_t v = (int32_t)value;
|
|
pack_fixed32(L, (uint8_t*)&v);
|
|
break;
|
|
}
|
|
case 'q':
|
|
{
|
|
int64_t v = (int64_t)value;
|
|
pack_fixed64(L, (uint8_t*)&v);
|
|
break;
|
|
}
|
|
case 'f':
|
|
{
|
|
float v = (float)value;
|
|
pack_fixed32(L, (uint8_t*)&v);
|
|
break;
|
|
}
|
|
case 'd':
|
|
{
|
|
double v = (double)value;
|
|
pack_fixed64(L, (uint8_t*)&v);
|
|
break;
|
|
}
|
|
case 'I':
|
|
{
|
|
uint32_t v = (uint32_t)value;
|
|
pack_fixed32(L, (uint8_t*)&v);
|
|
break;
|
|
}
|
|
case 'Q':
|
|
{
|
|
uint64_t v = (uint64_t) value;
|
|
pack_fixed64(L, (uint8_t*)&v);
|
|
break;
|
|
}
|
|
default:
|
|
luaL_error(L, "Unknown, format");
|
|
}
|
|
lua_call(L, 1, 0);
|
|
return 0;
|
|
}
|
|
|
|
static size_t size_varint(const char* buffer, size_t len)
|
|
{
|
|
size_t pos = 0;
|
|
while(buffer[pos] & 0x80){
|
|
++pos;
|
|
if(pos > len){
|
|
return -1;
|
|
}
|
|
}
|
|
return pos+1;
|
|
}
|
|
|
|
static uint64_t unpack_varint(const char* buffer, size_t len)
|
|
{
|
|
uint64_t value = buffer[0] & 0x7f;
|
|
size_t shift = 7;
|
|
size_t pos=0;
|
|
for(pos = 1; pos < len; ++pos)
|
|
{
|
|
value |= ((uint64_t)(buffer[pos] & 0x7f)) << shift;
|
|
shift += 7;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static int varint_decoder(lua_State *L)
|
|
{
|
|
size_t len;
|
|
const char* buffer = luaL_checklstring(L, 1, &len);
|
|
size_t pos = luaL_checkinteger(L, 2);
|
|
|
|
buffer += pos;
|
|
len = size_varint(buffer, len);
|
|
if(len == -1){
|
|
luaL_error(L, "error data %s, len:%d", buffer, len);
|
|
}else{
|
|
lua_pushnumber(L, (lua_Number)unpack_varint(buffer, len));
|
|
lua_pushinteger(L, len + pos);
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
static int signed_varint_decoder(lua_State *L)
|
|
{
|
|
size_t len;
|
|
const char* buffer = luaL_checklstring(L, 1, &len);
|
|
size_t pos = luaL_checkinteger(L, 2);
|
|
buffer += pos;
|
|
len = size_varint(buffer, len);
|
|
|
|
if(len == -1){
|
|
luaL_error(L, "error data %s, len:%d", buffer, len);
|
|
}else{
|
|
lua_pushnumber(L, (lua_Number)(int64_t)unpack_varint(buffer, len));
|
|
lua_pushinteger(L, len + pos);
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
static int zig_zag_encode32(lua_State *L)
|
|
{
|
|
int32_t n = luaL_checkinteger(L, 1);
|
|
uint32_t value = (n << 1) ^ (n >> 31);
|
|
lua_pushinteger(L, value);
|
|
return 1;
|
|
}
|
|
|
|
static int zig_zag_decode32(lua_State *L)
|
|
{
|
|
uint32_t n = (uint32_t)luaL_checkinteger(L, 1);
|
|
int32_t value = (n >> 1) ^ - (int32_t)(n & 1);
|
|
lua_pushinteger(L, value);
|
|
return 1;
|
|
}
|
|
|
|
static int zig_zag_encode64(lua_State *L)
|
|
{
|
|
int64_t n = (int64_t)luaL_checknumber(L, 1);
|
|
uint64_t value = (n << 1) ^ (n >> 63);
|
|
lua_pushinteger(L, value);
|
|
return 1;
|
|
}
|
|
|
|
static int zig_zag_decode64(lua_State *L)
|
|
{
|
|
uint64_t n = (uint64_t)luaL_checknumber(L, 1);
|
|
int64_t value = (n >> 1) ^ - (int64_t)(n & 1);
|
|
lua_pushinteger(L, value);
|
|
return 1;
|
|
}
|
|
|
|
static int read_tag(lua_State *L)
|
|
{
|
|
size_t len;
|
|
const char* buffer = luaL_checklstring(L, 1, &len);
|
|
size_t pos = luaL_checkinteger(L, 2);
|
|
|
|
buffer += pos;
|
|
len = size_varint(buffer, len);
|
|
if(len == -1){
|
|
luaL_error(L, "error data %s, len:%d", buffer, len);
|
|
}else{
|
|
lua_pushlstring(L, buffer, len);
|
|
lua_pushinteger(L, len + pos);
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
static const uint8_t* unpack_fixed32(const uint8_t* buffer, uint8_t* cache)
|
|
{
|
|
#ifdef IS_LITTLE_ENDIAN
|
|
return buffer;
|
|
#else
|
|
*(uint32_t*)cache = le32toh(*(uint32_t*)buffer);
|
|
return cache;
|
|
#endif
|
|
}
|
|
|
|
static const uint8_t* unpack_fixed64(const uint8_t* buffer, uint8_t* cache)
|
|
{
|
|
#ifdef IS_LITTLE_ENDIAN
|
|
return buffer;
|
|
#else
|
|
*(uint64_t*)cache = le64toh(*(uint64_t*)buffer);
|
|
return cache;
|
|
#endif
|
|
}
|
|
|
|
static int struct_unpack(lua_State *L)
|
|
{
|
|
uint8_t format = luaL_checkinteger(L, 1);
|
|
size_t len;
|
|
const uint8_t* buffer = (uint8_t*)luaL_checklstring(L, 2, &len);
|
|
size_t pos = luaL_checkinteger(L, 3);
|
|
|
|
buffer += pos;
|
|
uint8_t out[8];
|
|
switch(format){
|
|
case 'i':
|
|
{
|
|
lua_pushinteger(L, *(int32_t*)unpack_fixed32(buffer, out));
|
|
break;
|
|
}
|
|
case 'q':
|
|
{
|
|
lua_pushnumber(L, (lua_Number)*(int64_t*)unpack_fixed64(buffer, out));
|
|
break;
|
|
}
|
|
case 'f':
|
|
{
|
|
lua_pushnumber(L, (lua_Number)*(float*)unpack_fixed32(buffer, out));
|
|
break;
|
|
}
|
|
case 'd':
|
|
{
|
|
lua_pushnumber(L, (lua_Number)*(double*)unpack_fixed64(buffer, out));
|
|
break;
|
|
}
|
|
case 'I':
|
|
{
|
|
lua_pushnumber(L, *(uint32_t*)unpack_fixed32(buffer, out));
|
|
break;
|
|
}
|
|
case 'Q':
|
|
{
|
|
lua_pushnumber(L, (lua_Number)*(uint64_t*)unpack_fixed64(buffer, out));
|
|
break;
|
|
}
|
|
default:
|
|
luaL_error(L, "Unknown, format");
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int iostring_new(lua_State* L)
|
|
{
|
|
IOString* io = (IOString*)lua_newuserdata(L, sizeof(IOString));
|
|
io->size = 0;
|
|
|
|
luaL_getmetatable(L, IOSTRING_META);
|
|
lua_setmetatable(L, -2);
|
|
return 1;
|
|
}
|
|
|
|
static int iostring_str(lua_State* L)
|
|
{
|
|
IOString *io = checkiostring(L);
|
|
lua_pushlstring(L, io->buf, io->size);
|
|
return 1;
|
|
}
|
|
|
|
static int iostring_len(lua_State* L)
|
|
{
|
|
IOString *io = checkiostring(L);
|
|
lua_pushinteger(L, io->size);
|
|
return 1;
|
|
}
|
|
|
|
static int iostring_write(lua_State* L)
|
|
{
|
|
IOString *io = checkiostring(L);
|
|
size_t size;
|
|
const char* str = luaL_checklstring(L, 2, &size);
|
|
if(io->size + size > IOSTRING_BUF_LEN){
|
|
luaL_error(L, "Out of range");
|
|
}
|
|
memcpy(io->buf + io->size, str, size);
|
|
io->size += size;
|
|
return 0;
|
|
}
|
|
|
|
static int iostring_sub(lua_State* L)
|
|
{
|
|
IOString *io = checkiostring(L);
|
|
size_t begin = luaL_checkinteger(L, 2);
|
|
size_t end = luaL_checkinteger(L, 3);
|
|
|
|
if(begin > end || end > io->size)
|
|
{
|
|
luaL_error(L, "Out of range");
|
|
}
|
|
lua_pushlstring(L, io->buf + begin - 1, end - begin + 1);
|
|
return 1;
|
|
}
|
|
|
|
static int iostring_clear(lua_State* L)
|
|
{
|
|
IOString *io = checkiostring(L);
|
|
io->size = 0;
|
|
return 0;
|
|
}
|
|
|
|
static const struct luaL_reg _pb [] = {
|
|
{"varint_encoder", varint_encoder},
|
|
{"signed_varint_encoder", signed_varint_encoder},
|
|
{"read_tag", read_tag},
|
|
{"struct_pack", struct_pack},
|
|
{"struct_unpack", struct_unpack},
|
|
{"varint_decoder", varint_decoder},
|
|
{"signed_varint_decoder", signed_varint_decoder},
|
|
{"zig_zag_decode32", zig_zag_decode32},
|
|
{"zig_zag_encode32", zig_zag_encode32},
|
|
{"zig_zag_decode64", zig_zag_decode64},
|
|
{"zig_zag_encode64", zig_zag_encode64},
|
|
{"new_iostring", iostring_new},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const struct luaL_reg _c_iostring_m [] = {
|
|
{"__tostring", iostring_str},
|
|
{"__len", iostring_len},
|
|
{"write", iostring_write},
|
|
{"sub", iostring_sub},
|
|
{"clear", iostring_clear},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
int luaopen_pb (lua_State *L)
|
|
{
|
|
luaL_newmetatable(L, IOSTRING_META);
|
|
lua_pushvalue(L, -1);
|
|
lua_setfield(L, -2, "__index");
|
|
luaL_register(L, NULL, _c_iostring_m);
|
|
|
|
luaL_register(L, "pb", _pb);
|
|
return 1;
|
|
}
|