diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2014-01-05 12:05:58 -0200 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2014-01-05 12:05:58 -0200 |
| commit | 29256e8960d7f4bae728bb07a86eb0d604f5d98d (patch) | |
| tree | d41f0f0f136c25505ee93a90673730ade1c87127 /lstrlib.c | |
| parent | 438c534ff4c8ccad117354826d4ac133b84f2fdc (diff) | |
| download | lua-29256e8960d7f4bae728bb07a86eb0d604f5d98d.tar.gz lua-29256e8960d7f4bae728bb07a86eb0d604f5d98d.tar.bz2 lua-29256e8960d7f4bae728bb07a86eb0d604f5d98d.zip | |
first implementation of string.packint/string.unpackint
Diffstat (limited to 'lstrlib.c')
| -rw-r--r-- | lstrlib.c | 125 |
1 files changed, 124 insertions, 1 deletions
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | ** $Id: lstrlib.c,v 1.181 2013/06/19 14:29:01 roberto Exp roberto $ | 2 | ** $Id: lstrlib.c,v 1.182 2013/06/20 15:06:51 roberto Exp roberto $ |
| 3 | ** Standard library for string operations and pattern-matching | 3 | ** Standard library for string operations and pattern-matching |
| 4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
| 5 | */ | 5 | */ |
| @@ -938,6 +938,127 @@ static int str_format (lua_State *L) { | |||
| 938 | /* }====================================================== */ | 938 | /* }====================================================== */ |
| 939 | 939 | ||
| 940 | 940 | ||
| 941 | /* | ||
| 942 | ** {====================================================== | ||
| 943 | ** PACK/UNPACK | ||
| 944 | ** ======================================================= | ||
| 945 | */ | ||
| 946 | |||
| 947 | /* maximum size for the binary representation of an integer */ | ||
| 948 | #define MAXINTSIZE 8 | ||
| 949 | |||
| 950 | |||
| 951 | static union { | ||
| 952 | int dummy; | ||
| 953 | char little; /* true iff machine is little endian */ | ||
| 954 | } const nativeendian = {1}; | ||
| 955 | |||
| 956 | |||
| 957 | static int getendian (lua_State *L, int arg) { | ||
| 958 | const char *endian = luaL_optstring(L, arg, | ||
| 959 | (nativeendian.little ? "l" : "b")); | ||
| 960 | luaL_argcheck(L, *endian == 'l' || *endian == 'b', arg, | ||
| 961 | "endianess must be 'l' or 'b'"); | ||
| 962 | return (*endian == 'l'); | ||
| 963 | } | ||
| 964 | |||
| 965 | |||
| 966 | static int getintsize (lua_State *L, int arg) { | ||
| 967 | int size = luaL_optint(L, arg, sizeof(lua_Integer)); | ||
| 968 | luaL_argcheck(L, 1 <= size && size <= MAXINTSIZE, arg, | ||
| 969 | "integer size out of valid range"); | ||
| 970 | return size; | ||
| 971 | } | ||
| 972 | |||
| 973 | |||
| 974 | static int packint (char *buff, lua_Integer n, int littleendian, int size) { | ||
| 975 | int i; | ||
| 976 | if (littleendian) { | ||
| 977 | for (i = 0; i < size - 1; i++) { | ||
| 978 | buff[i] = (n & 0xff); | ||
| 979 | n >>= 8; | ||
| 980 | } | ||
| 981 | } | ||
| 982 | else { | ||
| 983 | for (i = size - 1; i > 0; i--) { | ||
| 984 | buff[i] = (n & 0xff); | ||
| 985 | n >>= 8; | ||
| 986 | } | ||
| 987 | } | ||
| 988 | buff[i] = (n & 0xff); /* last byte */ | ||
| 989 | /* test for overflow: OK if there are only zeros left in higher bytes, | ||
| 990 | or if there are only oneÅ› left and packed number is negative (signal | ||
| 991 | bit, the higher bit in last byte, is one) */ | ||
| 992 | return ((n & ~(lua_Integer)0xff) == 0 || (n | 0x7f) == ~(lua_Integer)0); | ||
| 993 | } | ||
| 994 | |||
| 995 | |||
| 996 | static int packint_l (lua_State *L) { | ||
| 997 | char buff[MAXINTSIZE]; | ||
| 998 | lua_Integer n = luaL_checkinteger(L, 1); | ||
| 999 | int size = getintsize(L, 2); | ||
| 1000 | int endian = getendian(L, 3); | ||
| 1001 | if (packint(buff, n, endian, size)) | ||
| 1002 | lua_pushlstring(L, buff, size); | ||
| 1003 | else | ||
| 1004 | luaL_error(L, "integer does not fit into given size (%d)", size); | ||
| 1005 | return 1; | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | |||
| 1009 | /* mask to check higher-order byte in a Lua integer */ | ||
| 1010 | #define HIGHERBYTE (~((lua_Unsigned)~0 >> 8)) | ||
| 1011 | |||
| 1012 | /* mask to check higher-order byte + signal bit of next byte */ | ||
| 1013 | #define HIGHERBYTE1 (~((lua_Unsigned)~0 >> 9)) | ||
| 1014 | |||
| 1015 | static int unpackint (const char *buff, lua_Integer *res, | ||
| 1016 | int littleendian, int size) { | ||
| 1017 | lua_Integer n = 0; | ||
| 1018 | int i; | ||
| 1019 | for (i = 0; i < size; i++) { | ||
| 1020 | if (i >= (int)sizeof(lua_Integer)) { /* will throw away a byte? */ | ||
| 1021 | /* check for overflow: it is OK to throw away leading zeros for a | ||
| 1022 | positive number; leading ones for a negative number; and one | ||
| 1023 | last leading zero to allow unsigned integers with a 1 in | ||
| 1024 | its "signal bit" */ | ||
| 1025 | if (!((n & HIGHERBYTE1) == 0 || /* zeros for pos. number */ | ||
| 1026 | (n & HIGHERBYTE1) == HIGHERBYTE1 || /* ones for neg. number */ | ||
| 1027 | ((n & HIGHERBYTE) == 0 && i == size - 1))) /* last zero */ | ||
| 1028 | return 0; /* overflow */ | ||
| 1029 | } | ||
| 1030 | n <<= 8; | ||
| 1031 | n |= (lua_Integer)(unsigned char)buff[littleendian ? size - 1 - i : i]; | ||
| 1032 | } | ||
| 1033 | if (size < (int)sizeof(lua_Integer)) { /* need sign extension? */ | ||
| 1034 | lua_Integer mask = (~(lua_Integer)0) << (size*8 - 1); | ||
| 1035 | if (n & mask) /* negative value? */ | ||
| 1036 | n |= mask; /* signal extension */ | ||
| 1037 | } | ||
| 1038 | *res = n; | ||
| 1039 | return 1; | ||
| 1040 | } | ||
| 1041 | |||
| 1042 | |||
| 1043 | static int unpackint_l (lua_State *L) { | ||
| 1044 | lua_Integer res; | ||
| 1045 | size_t len; | ||
| 1046 | const char *s = luaL_checklstring(L, 1, &len); | ||
| 1047 | lua_Integer pos = posrelat(luaL_optinteger(L, 2, 1), len); | ||
| 1048 | int size = getintsize(L, 3); | ||
| 1049 | int endian = getendian(L, 4); | ||
| 1050 | luaL_argcheck(L, 1 <= pos && (size_t)pos + size - 1 <= len, 1, | ||
| 1051 | "string too short"); | ||
| 1052 | if(unpackint(s + pos - 1, &res, endian, size)) | ||
| 1053 | lua_pushinteger(L, res); | ||
| 1054 | else | ||
| 1055 | luaL_error(L, "result does not fit into a Lua integer"); | ||
| 1056 | return 1; | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | /* }====================================================== */ | ||
| 1060 | |||
| 1061 | |||
| 941 | static const luaL_Reg strlib[] = { | 1062 | static const luaL_Reg strlib[] = { |
| 942 | {"byte", str_byte}, | 1063 | {"byte", str_byte}, |
| 943 | {"char", str_char}, | 1064 | {"char", str_char}, |
| @@ -953,6 +1074,8 @@ static const luaL_Reg strlib[] = { | |||
| 953 | {"reverse", str_reverse}, | 1074 | {"reverse", str_reverse}, |
| 954 | {"sub", str_sub}, | 1075 | {"sub", str_sub}, |
| 955 | {"upper", str_upper}, | 1076 | {"upper", str_upper}, |
| 1077 | {"packint", packint_l}, | ||
| 1078 | {"unpackint", unpackint_l}, | ||
| 956 | {NULL, NULL} | 1079 | {NULL, NULL} |
| 957 | }; | 1080 | }; |
| 958 | 1081 | ||
