Mercurial > prosody-hg
comparison util-src/struct.c @ 12354:3ce3633527af
util.struct: Import Roberto 'struct' library v0.3
Downloaded from http://www.inf.puc-rio.br/~roberto/struct/
This is for compatibility with Lua 5.2 (and 5.1). Eventually we can replace
this with string.pack/string.unpack which are available in 5.3+.
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Fri, 04 Mar 2022 15:03:02 +0000 |
| parents | |
| children | 05c250fa335a |
comparison
equal
deleted
inserted
replaced
| 12353:5ace23519e71 | 12354:3ce3633527af |
|---|---|
| 1 /* | |
| 2 ** {====================================================== | |
| 3 ** Library for packing/unpacking structures. | |
| 4 ** $Id: struct.c,v 1.8 2018/05/16 11:00:23 roberto Exp $ | |
| 5 ** See Copyright Notice at the end of this file | |
| 6 ** ======================================================= | |
| 7 */ | |
| 8 /* | |
| 9 ** Valid formats: | |
| 10 ** > - big endian | |
| 11 ** < - little endian | |
| 12 ** ![num] - alignment | |
| 13 ** x - pading | |
| 14 ** b/B - signed/unsigned byte | |
| 15 ** h/H - signed/unsigned short | |
| 16 ** l/L - signed/unsigned long | |
| 17 ** T - size_t | |
| 18 ** i/In - signed/unsigned integer with size 'n' (default is size of int) | |
| 19 ** cn - sequence of 'n' chars (from/to a string); when packing, n==0 means | |
| 20 the whole string; when unpacking, n==0 means use the previous | |
| 21 read number as the string length | |
| 22 ** s - zero-terminated string | |
| 23 ** f - float | |
| 24 ** d - double | |
| 25 ** ' ' - ignored | |
| 26 */ | |
| 27 | |
| 28 | |
| 29 #include <ctype.h> | |
| 30 #include <limits.h> | |
| 31 #include <stddef.h> | |
| 32 #include <string.h> | |
| 33 | |
| 34 | |
| 35 #include "lua.h" | |
| 36 #include "lauxlib.h" | |
| 37 | |
| 38 | |
| 39 #if (LUA_VERSION_NUM >= 502) | |
| 40 | |
| 41 #define luaL_register(L,n,f) luaL_newlib(L,f) | |
| 42 | |
| 43 #endif | |
| 44 | |
| 45 | |
| 46 /* basic integer type */ | |
| 47 #if !defined(STRUCT_INT) | |
| 48 #define STRUCT_INT long | |
| 49 #endif | |
| 50 | |
| 51 typedef STRUCT_INT Inttype; | |
| 52 | |
| 53 /* corresponding unsigned version */ | |
| 54 typedef unsigned STRUCT_INT Uinttype; | |
| 55 | |
| 56 | |
| 57 /* maximum size (in bytes) for integral types */ | |
| 58 #define MAXINTSIZE 32 | |
| 59 | |
| 60 /* is 'x' a power of 2? */ | |
| 61 #define isp2(x) ((x) > 0 && ((x) & ((x) - 1)) == 0) | |
| 62 | |
| 63 /* dummy structure to get alignment requirements */ | |
| 64 struct cD { | |
| 65 char c; | |
| 66 double d; | |
| 67 }; | |
| 68 | |
| 69 | |
| 70 #define PADDING (sizeof(struct cD) - sizeof(double)) | |
| 71 #define MAXALIGN (PADDING > sizeof(int) ? PADDING : sizeof(int)) | |
| 72 | |
| 73 | |
| 74 /* endian options */ | |
| 75 #define BIG 0 | |
| 76 #define LITTLE 1 | |
| 77 | |
| 78 | |
| 79 static union { | |
| 80 int dummy; | |
| 81 char endian; | |
| 82 } const native = {1}; | |
| 83 | |
| 84 | |
| 85 typedef struct Header { | |
| 86 int endian; | |
| 87 int align; | |
| 88 } Header; | |
| 89 | |
| 90 | |
| 91 static int getnum (const char **fmt, int df) { | |
| 92 if (!isdigit(**fmt)) /* no number? */ | |
| 93 return df; /* return default value */ | |
| 94 else { | |
| 95 int a = 0; | |
| 96 do { | |
| 97 a = a*10 + *((*fmt)++) - '0'; | |
| 98 } while (isdigit(**fmt)); | |
| 99 return a; | |
| 100 } | |
| 101 } | |
| 102 | |
| 103 | |
| 104 #define defaultoptions(h) ((h)->endian = native.endian, (h)->align = 1) | |
| 105 | |
| 106 | |
| 107 | |
| 108 static size_t optsize (lua_State *L, char opt, const char **fmt) { | |
| 109 switch (opt) { | |
| 110 case 'B': case 'b': return sizeof(char); | |
| 111 case 'H': case 'h': return sizeof(short); | |
| 112 case 'L': case 'l': return sizeof(long); | |
| 113 case 'T': return sizeof(size_t); | |
| 114 case 'f': return sizeof(float); | |
| 115 case 'd': return sizeof(double); | |
| 116 case 'x': return 1; | |
| 117 case 'c': return getnum(fmt, 1); | |
| 118 case 'i': case 'I': { | |
| 119 int sz = getnum(fmt, sizeof(int)); | |
| 120 if (sz > MAXINTSIZE) | |
| 121 luaL_error(L, "integral size %d is larger than limit of %d", | |
| 122 sz, MAXINTSIZE); | |
| 123 return sz; | |
| 124 } | |
| 125 default: return 0; /* other cases do not need alignment */ | |
| 126 } | |
| 127 } | |
| 128 | |
| 129 | |
| 130 /* | |
| 131 ** return number of bytes needed to align an element of size 'size' | |
| 132 ** at current position 'len' | |
| 133 */ | |
| 134 static int gettoalign (size_t len, Header *h, int opt, size_t size) { | |
| 135 if (size == 0 || opt == 'c') return 0; | |
| 136 if (size > (size_t)h->align) | |
| 137 size = h->align; /* respect max. alignment */ | |
| 138 return (size - (len & (size - 1))) & (size - 1); | |
| 139 } | |
| 140 | |
| 141 | |
| 142 /* | |
| 143 ** options to control endianess and alignment | |
| 144 */ | |
| 145 static void controloptions (lua_State *L, int opt, const char **fmt, | |
| 146 Header *h) { | |
| 147 switch (opt) { | |
| 148 case ' ': return; /* ignore white spaces */ | |
| 149 case '>': h->endian = BIG; return; | |
| 150 case '<': h->endian = LITTLE; return; | |
| 151 case '!': { | |
| 152 int a = getnum(fmt, MAXALIGN); | |
| 153 if (!isp2(a)) | |
| 154 luaL_error(L, "alignment %d is not a power of 2", a); | |
| 155 h->align = a; | |
| 156 return; | |
| 157 } | |
| 158 default: { | |
| 159 const char *msg = lua_pushfstring(L, "invalid format option '%c'", opt); | |
| 160 luaL_argerror(L, 1, msg); | |
| 161 } | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 | |
| 166 static void putinteger (lua_State *L, luaL_Buffer *b, int arg, int endian, | |
| 167 int size) { | |
| 168 lua_Number n = luaL_checknumber(L, arg); | |
| 169 Uinttype value; | |
| 170 char buff[MAXINTSIZE]; | |
| 171 if (n < 0) | |
| 172 value = (Uinttype)(Inttype)n; | |
| 173 else | |
| 174 value = (Uinttype)n; | |
| 175 if (endian == LITTLE) { | |
| 176 int i; | |
| 177 for (i = 0; i < size; i++) { | |
| 178 buff[i] = (value & 0xff); | |
| 179 value >>= 8; | |
| 180 } | |
| 181 } | |
| 182 else { | |
| 183 int i; | |
| 184 for (i = size - 1; i >= 0; i--) { | |
| 185 buff[i] = (value & 0xff); | |
| 186 value >>= 8; | |
| 187 } | |
| 188 } | |
| 189 luaL_addlstring(b, buff, size); | |
| 190 } | |
| 191 | |
| 192 | |
| 193 static void correctbytes (char *b, int size, int endian) { | |
| 194 if (endian != native.endian) { | |
| 195 int i = 0; | |
| 196 while (i < --size) { | |
| 197 char temp = b[i]; | |
| 198 b[i++] = b[size]; | |
| 199 b[size] = temp; | |
| 200 } | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 | |
| 205 static int b_pack (lua_State *L) { | |
| 206 luaL_Buffer b; | |
| 207 const char *fmt = luaL_checkstring(L, 1); | |
| 208 Header h; | |
| 209 int arg = 2; | |
| 210 size_t totalsize = 0; | |
| 211 defaultoptions(&h); | |
| 212 lua_pushnil(L); /* mark to separate arguments from string buffer */ | |
| 213 luaL_buffinit(L, &b); | |
| 214 while (*fmt != '\0') { | |
| 215 int opt = *fmt++; | |
| 216 size_t size = optsize(L, opt, &fmt); | |
| 217 int toalign = gettoalign(totalsize, &h, opt, size); | |
| 218 totalsize += toalign; | |
| 219 while (toalign-- > 0) luaL_addchar(&b, '\0'); | |
| 220 switch (opt) { | |
| 221 case 'b': case 'B': case 'h': case 'H': | |
| 222 case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */ | |
| 223 putinteger(L, &b, arg++, h.endian, size); | |
| 224 break; | |
| 225 } | |
| 226 case 'x': { | |
| 227 luaL_addchar(&b, '\0'); | |
| 228 break; | |
| 229 } | |
| 230 case 'f': { | |
| 231 float f = (float)luaL_checknumber(L, arg++); | |
| 232 correctbytes((char *)&f, size, h.endian); | |
| 233 luaL_addlstring(&b, (char *)&f, size); | |
| 234 break; | |
| 235 } | |
| 236 case 'd': { | |
| 237 double d = luaL_checknumber(L, arg++); | |
| 238 correctbytes((char *)&d, size, h.endian); | |
| 239 luaL_addlstring(&b, (char *)&d, size); | |
| 240 break; | |
| 241 } | |
| 242 case 'c': case 's': { | |
| 243 size_t l; | |
| 244 const char *s = luaL_checklstring(L, arg++, &l); | |
| 245 if (size == 0) size = l; | |
| 246 luaL_argcheck(L, l >= (size_t)size, arg, "string too short"); | |
| 247 luaL_addlstring(&b, s, size); | |
| 248 if (opt == 's') { | |
| 249 luaL_addchar(&b, '\0'); /* add zero at the end */ | |
| 250 size++; | |
| 251 } | |
| 252 break; | |
| 253 } | |
| 254 default: controloptions(L, opt, &fmt, &h); | |
| 255 } | |
| 256 totalsize += size; | |
| 257 } | |
| 258 luaL_pushresult(&b); | |
| 259 return 1; | |
| 260 } | |
| 261 | |
| 262 | |
| 263 static lua_Number getinteger (const char *buff, int endian, | |
| 264 int issigned, int size) { | |
| 265 Uinttype l = 0; | |
| 266 int i; | |
| 267 if (endian == BIG) { | |
| 268 for (i = 0; i < size; i++) { | |
| 269 l <<= 8; | |
| 270 l |= (Uinttype)(unsigned char)buff[i]; | |
| 271 } | |
| 272 } | |
| 273 else { | |
| 274 for (i = size - 1; i >= 0; i--) { | |
| 275 l <<= 8; | |
| 276 l |= (Uinttype)(unsigned char)buff[i]; | |
| 277 } | |
| 278 } | |
| 279 if (!issigned) | |
| 280 return (lua_Number)l; | |
| 281 else { /* signed format */ | |
| 282 Uinttype mask = (Uinttype)(~((Uinttype)0)) << (size*8 - 1); | |
| 283 if (l & mask) /* negative value? */ | |
| 284 l |= mask; /* signal extension */ | |
| 285 return (lua_Number)(Inttype)l; | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 | |
| 290 static int b_unpack (lua_State *L) { | |
| 291 Header h; | |
| 292 const char *fmt = luaL_checkstring(L, 1); | |
| 293 size_t ld; | |
| 294 const char *data = luaL_checklstring(L, 2, &ld); | |
| 295 size_t pos = (size_t)luaL_optinteger(L, 3, 1) - 1; | |
| 296 int n = 0; /* number of results */ | |
| 297 luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); | |
| 298 defaultoptions(&h); | |
| 299 while (*fmt) { | |
| 300 int opt = *fmt++; | |
| 301 size_t size = optsize(L, opt, &fmt); | |
| 302 pos += gettoalign(pos, &h, opt, size); | |
| 303 luaL_argcheck(L, size <= ld - pos, 2, "data string too short"); | |
| 304 /* stack space for item + next position */ | |
| 305 luaL_checkstack(L, 2, "too many results"); | |
| 306 switch (opt) { | |
| 307 case 'b': case 'B': case 'h': case 'H': | |
| 308 case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */ | |
| 309 int issigned = islower(opt); | |
| 310 lua_Number res = getinteger(data+pos, h.endian, issigned, size); | |
| 311 lua_pushnumber(L, res); n++; | |
| 312 break; | |
| 313 } | |
| 314 case 'x': { | |
| 315 break; | |
| 316 } | |
| 317 case 'f': { | |
| 318 float f; | |
| 319 memcpy(&f, data+pos, size); | |
| 320 correctbytes((char *)&f, sizeof(f), h.endian); | |
| 321 lua_pushnumber(L, f); n++; | |
| 322 break; | |
| 323 } | |
| 324 case 'd': { | |
| 325 double d; | |
| 326 memcpy(&d, data+pos, size); | |
| 327 correctbytes((char *)&d, sizeof(d), h.endian); | |
| 328 lua_pushnumber(L, d); n++; | |
| 329 break; | |
| 330 } | |
| 331 case 'c': { | |
| 332 if (size == 0) { | |
| 333 if (n == 0 || !lua_isnumber(L, -1)) | |
| 334 luaL_error(L, "format 'c0' needs a previous size"); | |
| 335 size = lua_tonumber(L, -1); | |
| 336 lua_pop(L, 1); n--; | |
| 337 luaL_argcheck(L, size <= ld - pos, 2, "data string too short"); | |
| 338 } | |
| 339 lua_pushlstring(L, data+pos, size); n++; | |
| 340 break; | |
| 341 } | |
| 342 case 's': { | |
| 343 const char *e = (const char *)memchr(data+pos, '\0', ld - pos); | |
| 344 if (e == NULL) | |
| 345 luaL_error(L, "unfinished string in data"); | |
| 346 size = (e - (data+pos)) + 1; | |
| 347 lua_pushlstring(L, data+pos, size - 1); n++; | |
| 348 break; | |
| 349 } | |
| 350 default: controloptions(L, opt, &fmt, &h); | |
| 351 } | |
| 352 pos += size; | |
| 353 } | |
| 354 lua_pushinteger(L, pos + 1); /* next position */ | |
| 355 return n + 1; | |
| 356 } | |
| 357 | |
| 358 | |
| 359 static int b_size (lua_State *L) { | |
| 360 Header h; | |
| 361 const char *fmt = luaL_checkstring(L, 1); | |
| 362 size_t pos = 0; | |
| 363 defaultoptions(&h); | |
| 364 while (*fmt) { | |
| 365 int opt = *fmt++; | |
| 366 size_t size = optsize(L, opt, &fmt); | |
| 367 pos += gettoalign(pos, &h, opt, size); | |
| 368 if (opt == 's') | |
| 369 luaL_argerror(L, 1, "option 's' has no fixed size"); | |
| 370 else if (opt == 'c' && size == 0) | |
| 371 luaL_argerror(L, 1, "option 'c0' has no fixed size"); | |
| 372 if (!isalnum(opt)) | |
| 373 controloptions(L, opt, &fmt, &h); | |
| 374 pos += size; | |
| 375 } | |
| 376 lua_pushinteger(L, pos); | |
| 377 return 1; | |
| 378 } | |
| 379 | |
| 380 /* }====================================================== */ | |
| 381 | |
| 382 | |
| 383 | |
| 384 static const struct luaL_Reg thislib[] = { | |
| 385 {"pack", b_pack}, | |
| 386 {"unpack", b_unpack}, | |
| 387 {"size", b_size}, | |
| 388 {NULL, NULL} | |
| 389 }; | |
| 390 | |
| 391 | |
| 392 LUALIB_API int luaopen_util_struct (lua_State *L); | |
| 393 | |
| 394 LUALIB_API int luaopen_util_struct (lua_State *L) { | |
| 395 luaL_register(L, "struct", thislib); | |
| 396 return 1; | |
| 397 } | |
| 398 | |
| 399 | |
| 400 /****************************************************************************** | |
| 401 * Copyright (C) 2010-2018 Lua.org, PUC-Rio. All rights reserved. | |
| 402 * | |
| 403 * Permission is hereby granted, free of charge, to any person obtaining | |
| 404 * a copy of this software and associated documentation files (the | |
| 405 * "Software"), to deal in the Software without restriction, including | |
| 406 * without limitation the rights to use, copy, modify, merge, publish, | |
| 407 * distribute, sublicense, and/or sell copies of the Software, and to | |
| 408 * permit persons to whom the Software is furnished to do so, subject to | |
| 409 * the following conditions: | |
| 410 * | |
| 411 * The above copyright notice and this permission notice shall be | |
| 412 * included in all copies or substantial portions of the Software. | |
| 413 * | |
| 414 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 415 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| 416 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
| 417 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
| 418 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
| 419 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
| 420 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| 421 ******************************************************************************/ | |
| 422 |
