blob: cdfb72a9d9941261d0d18f7c4db243381586389a [file] [log] [blame]
Roger Meier6cf0ffc2014-04-05 00:45:42 +02001//
2// Licensed to the Apache Software Foundation (ASF) under one
3// or more contributor license agreements. See the NOTICE file
4// distributed with this work for additional information
5// regarding copyright ownership. The ASF licenses this file
6// to you under the Apache License, Version 2.0 (the
7// "License"); you may not use this file except in compliance
8// with the License. You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing,
13// software distributed under the License is distributed on an
14// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15// KIND, either express or implied. See the License for the
16// specific language governing permissions and limitations
17// under the License.
18//
19
20#include <lua.h>
21#include <lauxlib.h>
22#include <string.h>
23#include <inttypes.h>
24#include <netinet/in.h>
25
26extern int64_t lualongnumber_checklong(lua_State *L, int index);
27extern int64_t lualongnumber_pushlong(lua_State *L, int64_t *val);
28
29// host order to network order (64-bit)
30static int64_t T_htonll(uint64_t data) {
31 uint32_t d1 = htonl((uint32_t)data);
32 uint32_t d2 = htonl((uint32_t)(data >> 32));
33 return ((uint64_t)d1 << 32) + (uint64_t)d2;
34}
35
36// network order to host order (64-bit)
37static int64_t T_ntohll(uint64_t data) {
38 uint32_t d1 = ntohl((uint32_t)data);
39 uint32_t d2 = ntohl((uint32_t)(data >> 32));
40 return ((uint64_t)d1 << 32) + (uint64_t)d2;
41}
42
43/**
44 * bpack(type, data)
45 * c - Signed Byte
46 * s - Signed Short
47 * i - Signed Int
Thomaseb684d32024-07-28 15:32:23 +020048 * I - Unsigned Int
Roger Meier6cf0ffc2014-04-05 00:45:42 +020049 * l - Signed Long
50 * d - Double
51 */
52static int l_bpack(lua_State *L) {
53 const char *code = luaL_checkstring(L, 1);
54 luaL_argcheck(L, code[1] == '\0', 0, "Format code must be one character.");
55 luaL_Buffer buf;
56 luaL_buffinit(L, &buf);
57
58 switch (code[0]) {
59 case 'c': {
60 int8_t data = luaL_checknumber(L, 2);
61 luaL_addlstring(&buf, (void*)&data, sizeof(data));
62 break;
63 }
64 case 's': {
65 int16_t data = luaL_checknumber(L, 2);
66 data = (int16_t)htons(data);
67 luaL_addlstring(&buf, (void*)&data, sizeof(data));
68 break;
69 }
70 case 'i': {
71 int32_t data = luaL_checkinteger(L, 2);
72 data = (int32_t)htonl(data);
73 luaL_addlstring(&buf, (void*)&data, sizeof(data));
74 break;
75 }
Thomaseb684d32024-07-28 15:32:23 +020076 case 'I': {
77 uint32_t data = luaL_checkinteger(L, 2);
78 data = (uint32_t)htonl(data);
79 luaL_addlstring(&buf, (void*)&data, sizeof(data));
80 break;
81 }
Roger Meier6cf0ffc2014-04-05 00:45:42 +020082 case 'l': {
83 int64_t data = lualongnumber_checklong(L, 2);
84 data = (int64_t)T_htonll(data);
85 luaL_addlstring(&buf, (void*)&data, sizeof(data));
86 break;
87 }
88 case 'd': {
89 double data = luaL_checknumber(L, 2);
90 luaL_addlstring(&buf, (void*)&data, sizeof(data));
91 break;
92 }
93 default:
94 luaL_argcheck(L, 0, 0, "Invalid format code.");
95 }
96
97 luaL_pushresult(&buf);
98 return 1;
99}
100
101/**
102 * bunpack(type, data)
103 * c - Signed Byte
WangYaofuc1a78ba2016-01-28 19:29:54 +0800104 * C - Unsigned Byte
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200105 * s - Signed Short
106 * i - Signed Int
Thomaseb684d32024-07-28 15:32:23 +0200107 * I - Unsigned Int
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200108 * l - Signed Long
109 * d - Double
110 */
111static int l_bunpack(lua_State *L) {
112 const char *code = luaL_checkstring(L, 1);
113 luaL_argcheck(L, code[1] == '\0', 0, "Format code must be one character.");
114 const char *data = luaL_checkstring(L, 2);
James E. King, III3641b542017-04-06 17:48:23 -0400115#if LUA_VERSION_NUM >= 502
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200116 size_t len = lua_rawlen(L, 2);
James E. King, III3641b542017-04-06 17:48:23 -0400117#else
118 size_t len = lua_objlen(L, 2);
WangYaofuc1a78ba2016-01-28 19:29:54 +0800119#endif
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200120
121 switch (code[0]) {
122 case 'c': {
123 int8_t val;
124 luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
125 memcpy(&val, data, sizeof(val));
126 lua_pushnumber(L, val);
127 break;
128 }
WangYaofuc1a78ba2016-01-28 19:29:54 +0800129 /**
130 * unpack unsigned Byte.
131 */
132 case 'C': {
133 uint8_t val;
134 luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
135 memcpy(&val, data, sizeof(val));
136 lua_pushnumber(L, val);
137 break;
138 }
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200139 case 's': {
140 int16_t val;
141 luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
142 memcpy(&val, data, sizeof(val));
143 val = (int16_t)ntohs(val);
144 lua_pushnumber(L, val);
145 break;
146 }
147 case 'i': {
148 int32_t val;
149 luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
150 memcpy(&val, data, sizeof(val));
151 val = (int32_t)ntohl(val);
152 lua_pushnumber(L, val);
153 break;
154 }
Thomaseb684d32024-07-28 15:32:23 +0200155 /**
156 * unpack unsigned Int.
157 */
158 case 'I': {
159 uint32_t val;
160 luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
161 memcpy(&val, data, sizeof(val));
162 val = (uint32_t)ntohl(val);
163 lua_pushnumber(L, val);
164 break;
165 }
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200166 case 'l': {
167 int64_t val;
168 luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
169 memcpy(&val, data, sizeof(val));
170 val = (int64_t)T_ntohll(val);
171 lualongnumber_pushlong(L, &val);
172 break;
173 }
174 case 'd': {
175 double val;
176 luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
177 memcpy(&val, data, sizeof(val));
178 lua_pushnumber(L, val);
179 break;
180 }
181 default:
182 luaL_argcheck(L, 0, 0, "Invalid format code.");
183 }
184 return 1;
185}
186
WangYaofuc1a78ba2016-01-28 19:29:54 +0800187/**
188 * Convert l into a zigzag long. This allows negative numbers to be
189 * represented compactly as a varint.
190 */
191static int l_i64ToZigzag(lua_State *L) {
192 int64_t n = lualongnumber_checklong(L, 1);
193 int64_t result = (n << 1) ^ (n >> 63);
194 lualongnumber_pushlong(L, &result);
195 return 1;
196}
197/**
198 * Convert n into a zigzag int. This allows negative numbers to be
199 * represented compactly as a varint.
200 */
201static int l_i32ToZigzag(lua_State *L) {
202 int32_t n = luaL_checkinteger(L, 1);
203 uint32_t result = (uint32_t)(n << 1) ^ (n >> 31);
204 lua_pushnumber(L, result);
205 return 1;
206}
207
208/**
209 * Convert from zigzag int to int.
210 */
211static int l_zigzagToI32(lua_State *L) {
212 uint32_t n = luaL_checkinteger(L, 1);
213 int32_t result = (int32_t)(n >> 1) ^ (uint32_t)(-(int32_t)(n & 1));
214 lua_pushnumber(L, result);
215 return 1;
216}
217
218/**
219 * Convert from zigzag long to long.
220 */
221static int l_zigzagToI64(lua_State *L) {
222 int64_t n = lualongnumber_checklong(L, 1);
223 int64_t result = (int64_t)(n >> 1) ^ (uint64_t)(-(int64_t)(n & 1));
224 lualongnumber_pushlong(L, &result);
225 return 1;
226}
227
228/**
229 * Convert an i32 to a varint. Results in 1-5 bytes on the buffer.
230 */
231static int l_toVarint32(lua_State *L) {
232 uint8_t buf[5];
233 uint32_t n = luaL_checkinteger(L, 1);
234 uint32_t wsize = 0;
235
236 while (1) {
237 if ((n & ~0x7F) == 0) {
238 buf[wsize++] = (int8_t)n;
239 break;
240 } else {
241 buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
242 n >>= 7;
243 }
244 }
245 lua_pushlstring(L, buf, wsize);
246 return 1;
247}
248
249/**
250 * Convert an i64 to a varint. Results in 1-10 bytes on the buffer.
251 */
252static int l_toVarint64(lua_State *L) {
253 uint8_t data[10];
254 uint64_t n = lualongnumber_checklong(L, 1);
255 uint32_t wsize = 0;
256 luaL_Buffer buf;
257 luaL_buffinit(L, &buf);
258
259 while (1) {
260 if ((n & ~0x7FL) == 0) {
261 data[wsize++] = (int8_t)n;
262 break;
263 } else {
264 data[wsize++] = (int8_t)((n & 0x7F) | 0x80);
265 n >>= 7;
266 }
267 }
268
269 luaL_addlstring(&buf, (void*)&data, wsize);
270 luaL_pushresult(&buf);
271 return 1;
272}
273
274/**
275 * Convert a varint to i64.
276 */
277static int l_fromVarint64(lua_State *L) {
278 int64_t result;
279 uint8_t byte = luaL_checknumber(L, 1);
280 int32_t shift = luaL_checknumber(L, 2);
281 uint64_t n = (uint64_t)lualongnumber_checklong(L, 3);
282 n |= (uint64_t)(byte & 0x7f) << shift;
283
284 if (!(byte & 0x80)) {
285 result = (int64_t)(n >> 1) ^ (uint64_t)(-(int64_t)(n & 1));
286 lua_pushnumber(L, 0);
287 } else {
288 result = n;
289 lua_pushnumber(L, 1);
290 }
291 lualongnumber_pushlong(L, &result);
292 return 2;
293}
294
295/**
296 * To pack message type of compact protocol.
297 */
298static int l_packMesgType(lua_State *L) {
299 int32_t version_n = luaL_checkinteger(L, 1);
300 int32_t version_mask = luaL_checkinteger(L, 2);
301 int32_t messagetype = luaL_checkinteger(L, 3);
302 int32_t type_shift_amount = luaL_checkinteger(L, 4);
303 int32_t type_mask = luaL_checkinteger(L, 5);
304 int32_t to_mesg_type = (version_n & version_mask) |
305 (((int32_t)messagetype << type_shift_amount) & type_mask);
306 lua_pushnumber(L, to_mesg_type);
307 return 1;
308}
309
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200310static const struct luaL_Reg lua_bpack[] = {
311 {"bpack", l_bpack},
312 {"bunpack", l_bunpack},
WangYaofuc1a78ba2016-01-28 19:29:54 +0800313 {"i32ToZigzag", l_i32ToZigzag},
314 {"i64ToZigzag", l_i64ToZigzag},
315 {"zigzagToI32", l_zigzagToI32},
316 {"zigzagToI64", l_zigzagToI64},
317 {"toVarint32", l_toVarint32},
318 {"toVarint64", l_toVarint64},
319 {"fromVarint64", l_fromVarint64},
320 {"packMesgType", l_packMesgType},
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200321 {NULL, NULL}
322};
323
324int luaopen_libluabpack(lua_State *L) {
Thomaseb684d32024-07-28 15:32:23 +0200325#if LUA_VERSION_NUM >= 502
326 lua_newtable(L);
327 luaL_setfuncs(L, lua_bpack, 0);
328#else
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200329 luaL_register(L, "libluabpack", lua_bpack);
Thomaseb684d32024-07-28 15:32:23 +0200330#endif
Roger Meier6cf0ffc2014-04-05 00:45:42 +0200331 return 1;
332}