root/src/modules/lua_lpack.c

Revision 2bc357abaca0cfec2814a8e2892c5c9775e46dfc, 5.4 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

add a string.pack and string.unpack.

this code is public domain, I've reviewed it and it should be safe. It's
a variant on a plethora of other implementations out there and would be
trivial to rewrite should the need arise.

  • Property mode set to 100644
Line 
1 /*
2 * lpack.c
3 * a Lua library for packing and unpacking binary data
4 * Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br>
5 * 29 Jun 2007 19:27:20
6 * This code is hereby placed in the public domain.
7 * with contributions from Ignacio CastaÒo <castanyo@yahoo.es> and
8 * Roberto Ierusalimschy <roberto@inf.puc-rio.br>.
9 */
10
11 #define OP_ZSTRING      'z'             /* zero-terminated string */
12 #define OP_BSTRING      'p'             /* string preceded by length byte */
13 #define OP_WSTRING      'P'             /* string preceded by length word */
14 #define OP_SSTRING      'a'             /* string preceded by length size_t */
15 #define OP_STRING       'A'             /* string */
16 #define OP_FLOAT        'f'             /* float */
17 #define OP_DOUBLE       'd'             /* double */
18 #define OP_NUMBER       'n'             /* Lua number */
19 #define OP_CHAR         'c'             /* char */
20 #define OP_BYTE         'b'             /* byte = unsigned char */
21 #define OP_SHORT        'h'             /* short */
22 #define OP_USHORT       'H'             /* unsigned short */
23 #define OP_INT          'i'             /* int */
24 #define OP_UINT         'I'             /* unsigned int */
25 #define OP_LONG         'l'             /* long */
26 #define OP_ULONG        'L'             /* unsigned long */
27 #define OP_LITTLEENDIAN '<'             /* little endian */
28 #define OP_BIGENDIAN    '>'             /* big endian */
29 #define OP_NATIVE       '='             /* native endian */
30
31 #include <ctype.h>
32 #include <string.h>
33
34 #include "lua_noit.h"
35
36 static void badcode(lua_State *L, int c)
37 {
38  char s[]="bad code `?'";
39  s[sizeof(s)-3]=c;
40  luaL_argerror(L,1,s);
41 }
42
43 static int doendian(int c)
44 {
45  int x=1;
46  int e=*(char*)&x;
47  if (c==OP_LITTLEENDIAN) return !e;
48  if (c==OP_BIGENDIAN) return e;
49  if (c==OP_NATIVE) return 0;
50  return 0;
51 }
52
53 static void doswap(int swap, void *p, size_t n)
54 {
55  if (swap)
56  {
57   char *a=p;
58   int i,j;
59   for (i=0, j=n-1, n=n/2; n--; i++, j--)
60   {
61    char t=a[i]; a[i]=a[j]; a[j]=t;
62   }
63  }
64 }
65
66 #define UNPACKNUMBER(OP,T)              \
67    case OP:                             \
68    {                                    \
69     T a;                                \
70     int m=sizeof(a);                    \
71     if (i+m>len) goto done;             \
72     memcpy(&a,s+i,m);                   \
73     i+=m;                               \
74     doswap(swap,&a,m);                  \
75     lua_pushnumber(L,(lua_Number)a);    \
76     ++n;                                \
77     break;                              \
78    }
79
80 #define UNPACKSTRING(OP,T)              \
81    case OP:                             \
82    {                                    \
83     T l;                                \
84     int m=sizeof(l);                    \
85     if (i+m>len) goto done;             \
86     memcpy(&l,s+i,m);                   \
87     doswap(swap,&l,m);                  \
88     if (i+m+l>len) goto done;           \
89     i+=m;                               \
90     lua_pushlstring(L,s+i,l);           \
91     i+=l;                               \
92     ++n;                                \
93     break;                              \
94    }
95
96 static int l_unpack(lua_State *L)               /** unpack(s,f,[init]) */
97 {
98  size_t len;
99  const char *s=luaL_checklstring(L,1,&len);
100  const char *f=luaL_checkstring(L,2);
101  int i=luaL_optnumber(L,3,1)-1;
102  int n=0;
103  int swap=0;
104  lua_pushnil(L);
105  while (*f)
106  {
107   int c=*f++;
108   int N=1;
109   if (isdigit(*f))
110   {
111    N=0;
112    while (isdigit(*f)) N=10*N+(*f++)-'0';
113    if (N==0 && c==OP_STRING) { lua_pushliteral(L,""); ++n; }
114   }
115   while (N--) switch (c)
116   {
117    case OP_LITTLEENDIAN:
118    case OP_BIGENDIAN:
119    case OP_NATIVE:
120    {
121     swap=doendian(c);
122     N=0;
123     break;
124    }
125    case OP_STRING:
126    {
127     ++N;
128     if (i+N>len) goto done;
129     lua_pushlstring(L,s+i,N);
130     i+=N;
131     ++n;
132     N=0;
133     break;
134    }
135    case OP_ZSTRING:
136    {
137     size_t l;
138     if (i>=len) goto done;
139     l=strlen(s+i);
140     lua_pushlstring(L,s+i,l);
141     i+=l+1;
142     ++n;
143     break;
144    }
145    UNPACKSTRING(OP_BSTRING, uint8_t)
146    UNPACKSTRING(OP_WSTRING, int16_t)
147    UNPACKSTRING(OP_SSTRING, size_t)
148    UNPACKNUMBER(OP_NUMBER, lua_Number)
149    UNPACKNUMBER(OP_DOUBLE, double)
150    UNPACKNUMBER(OP_FLOAT, float)
151    UNPACKNUMBER(OP_CHAR, char)
152    UNPACKNUMBER(OP_BYTE, uint8_t)
153    UNPACKNUMBER(OP_SHORT, int16_t)
154    UNPACKNUMBER(OP_USHORT, uint16_t)
155    UNPACKNUMBER(OP_INT, int32_t)
156    UNPACKNUMBER(OP_UINT, uint32_t)
157    UNPACKNUMBER(OP_LONG, int64_t)
158    UNPACKNUMBER(OP_ULONG, uint64_t)
159    case ' ': case ',':
160     break;
161    default:
162     badcode(L,c);
163     break;
164   }
165  }
166 done:
167  lua_pushnumber(L,i+1);
168  lua_replace(L,-n-2);
169  return n+1;
170 }
171
172 #define PACKNUMBER(OP,T)                        \
173    case OP:                                     \
174    {                                            \
175     T a=(T)luaL_checknumber(L,i++);             \
176     doswap(swap,&a,sizeof(a));                  \
177     luaL_addlstring(&b,(void*)&a,sizeof(a));    \
178     break;                                      \
179    }
180
181 #define PACKSTRING(OP,T)                        \
182    case OP:                                     \
183    {                                            \
184     size_t l;                                   \
185     const char *a=luaL_checklstring(L,i++,&l);  \
186     T ll=(T)l;                                  \
187     doswap(swap,&ll,sizeof(ll));                \
188     luaL_addlstring(&b,(void*)&ll,sizeof(ll));  \
189     luaL_addlstring(&b,a,l);                    \
190     break;                                      \
191    }
192
193 static int l_pack(lua_State *L)                 /** pack(f,...) */
194 {
195  int i=2;
196  const char *f=luaL_checkstring(L,1);
197  int swap=0;
198  luaL_Buffer b;
199  luaL_buffinit(L,&b);
200  while (*f)
201  {
202   int c=*f++;
203   int N=1;
204   if (isdigit(*f))
205   {
206    N=0;
207    while (isdigit(*f)) N=10*N+(*f++)-'0';
208   }
209   while (N--) switch (c)
210   {
211    case OP_LITTLEENDIAN:
212    case OP_BIGENDIAN:
213    case OP_NATIVE:
214    {
215     swap=doendian(c);
216     N=0;
217     break;
218    }
219    case OP_STRING:
220    case OP_ZSTRING:
221    {
222     size_t l;
223     const char *a=luaL_checklstring(L,i++,&l);
224     luaL_addlstring(&b,a,l+(c==OP_ZSTRING));
225     break;
226    }
227    PACKSTRING(OP_BSTRING, uint8_t)
228    PACKSTRING(OP_WSTRING, uint16_t)
229    PACKSTRING(OP_SSTRING, size_t)
230    PACKNUMBER(OP_NUMBER, lua_Number)
231    PACKNUMBER(OP_DOUBLE, double)
232    PACKNUMBER(OP_FLOAT, float)
233    PACKNUMBER(OP_CHAR, char)
234    PACKNUMBER(OP_BYTE, uint8_t)
235    PACKNUMBER(OP_SHORT, int16_t)
236    PACKNUMBER(OP_USHORT, uint16_t)
237    PACKNUMBER(OP_INT, int32_t)
238    PACKNUMBER(OP_UINT, uint32_t)
239    PACKNUMBER(OP_LONG, int64_t)
240    PACKNUMBER(OP_ULONG, uint64_t)
241    case ' ': case ',':
242     break;
243    default:
244     badcode(L,c);
245     break;
246   }
247  }
248  luaL_pushresult(&b);
249  return 1;
250 }
251
252 static const luaL_reg R[] =
253 {
254         {"pack",        l_pack},
255         {"unpack",      l_unpack},
256         {NULL,  NULL}
257 };
258
259 int luaopen_pack(lua_State *L)
260 {
261  luaL_openlib(L, LUA_STRLIBNAME, R, 0);
262  return 0;
263 }
264
Note: See TracBrowser for help on using the browser.