pparseint.h (15931B)
1 #ifndef PPARSEINT_H 2 #define PPARSEINT_H 3 4 #ifdef __cplusplus 5 extern "C" { 6 #endif 7 8 /* 9 * Type specific integer parsers: 10 * 11 * const char * 12 * parse_<type-name>(const char *buf, size_t len, <type> *value, int *status); 13 * 14 * parse_uint64, parse_int64 15 * parse_uint32, parse_int32 16 * parse_uint16, parse_int16 17 * parse_uint8, parse_int8 18 * parse_ushort, parse_short 19 * parse_uint, parse_int 20 * parse_ulong, parse_long 21 * 22 * Leading space must be stripped in advance. Status argument can be 23 * null. 24 * 25 * Returns pointer to end of match and a non-negative status code 26 * on succcess (0 for unsigned, 1 for signed): 27 * 28 * PARSE_INTEGER_UNSIGNED 29 * PARSE_INTEGER_SIGNED 30 * 31 * Returns null with a negative status code and unmodified value on 32 * invalid integer formats: 33 * 34 * PARSE_INTEGER_OVERFLOW 35 * PARSE_INTEGER_UNDERFLOW 36 * PARSE_INTEGER_INVALID 37 * 38 * Returns input buffer with negative status code and unmodified value 39 * if first character does not start an integer (not a sign or a digit). 40 * 41 * PARSE_INTEGER_UNMATCHED 42 * PARSE_INTEGER_END 43 * 44 * The signed parsers only works with two's complement architectures. 45 * 46 * Note: the corresponding parse_float and parse_double parsers do not 47 * have a status argument because +/-Inf and NaN are conventionally used 48 * for this. 49 */ 50 51 #include "limits.h" 52 #ifndef UINT8_MAX 53 #include <stdint.h> 54 #endif 55 56 #define PARSE_INTEGER_UNSIGNED 0 57 #define PARSE_INTEGER_SIGNED 1 58 #define PARSE_INTEGER_OVERFLOW -1 59 #define PARSE_INTEGER_UNDERFLOW -2 60 #define PARSE_INTEGER_INVALID -3 61 #define PARSE_INTEGER_UNMATCHED -4 62 #define PARSE_INTEGER_END -5 63 64 /* 65 * Generic integer parser that holds 64-bit unsigned values and stores 66 * sign separately. Leading space is not valid. 67 * 68 * Note: this function differs from the type specific parsers like 69 * parse_int64 by not negating the value when there is a sign. It 70 * differs from parse_uint64 by being able to return a negative 71 * UINT64_MAX successfully. 72 * 73 * This parser is used by all type specific integer parsers. 74 * 75 * Status argument can be null. 76 */ 77 static const char *parse_integer(const char *buf, size_t len, uint64_t *value, int *status) 78 { 79 uint64_t x0, x = 0; 80 const char *k, *end = buf + len; 81 int sign, status_; 82 83 if (!status) { 84 status = &status_; 85 } 86 if (buf == end) { 87 *status = PARSE_INTEGER_END; 88 return buf; 89 } 90 k = buf; 91 sign = *buf == '-'; 92 buf += sign; 93 while (buf != end && *buf >= '0' && *buf <= '9') { 94 x0 = x; 95 x = x * 10 + (uint64_t)(*buf - '0'); 96 if (x0 > x) { 97 *status = sign ? PARSE_INTEGER_UNDERFLOW : PARSE_INTEGER_OVERFLOW; 98 return 0; 99 } 100 ++buf; 101 } 102 if (buf == k) { 103 /* No number was matched, but it isn't an invalid number either. */ 104 *status = PARSE_INTEGER_UNMATCHED; 105 return buf; 106 } 107 if (buf == k + sign) { 108 *status = PARSE_INTEGER_INVALID; 109 return 0; 110 } 111 if (buf != end) 112 switch (*buf) { 113 case 'e': case 'E': case '.': case 'p': case 'P': 114 *status = PARSE_INTEGER_INVALID; 115 return 0; 116 } 117 *value = x; 118 *status = sign; 119 return buf; 120 } 121 122 /* 123 * Parse hex values like 0xff, -0xff, 0XdeAdBeaf42, cannot be trailed by '.', 'p', or 'P'. 124 * Overflows if string is more than 16 valid hex digits. Otherwise similar to parse_integer. 125 */ 126 static const char *parse_hex_integer(const char *buf, size_t len, uint64_t *value, int *status) 127 { 128 uint64_t x = 0; 129 const char *k, *k2, *end = buf + len; 130 int sign, status_; 131 unsigned char c; 132 133 if (!status) { 134 status = &status_; 135 } 136 if (buf == end) { 137 *status = PARSE_INTEGER_END; 138 return buf; 139 } 140 sign = *buf == '-'; 141 buf += sign; 142 if (end - buf < 2 || buf[0] != '0' || (buf[1] | 0x20) != 'x') { 143 *status = PARSE_INTEGER_UNMATCHED; 144 return buf - sign; 145 } 146 buf += 2; 147 k = buf; 148 k2 = end; 149 if (end - buf > 16) { 150 k2 = buf + 16; 151 } 152 while (buf != k2) { 153 c = (unsigned char)*buf; 154 if (c >= '0' && c <= '9') { 155 x = x * 16 + c - '0'; 156 } else { 157 /* Lower case. */ 158 c |= 0x20; 159 if (c >= 'a' && c <= 'f') { 160 x = x * 16 + c - 'a' + 10; 161 } else { 162 break; 163 } 164 } 165 ++buf; 166 } 167 if (buf == k) { 168 if (sign) { 169 *status = PARSE_INTEGER_INVALID; 170 return 0; 171 } else { 172 /* No number was matched, but it isn't an invalid number either. */ 173 *status = PARSE_INTEGER_UNMATCHED; 174 return buf; 175 } 176 } 177 if (buf == end) { 178 goto done; 179 } 180 c = (unsigned char)*buf; 181 if (buf == k2) { 182 if (c >= '0' && c <= '9') { 183 *status = sign ? PARSE_INTEGER_UNDERFLOW : PARSE_INTEGER_OVERFLOW; 184 return 0; 185 } 186 c |= 0x20; 187 if (c >= 'a' && c <= 'f') { 188 *status = sign ? PARSE_INTEGER_UNDERFLOW : PARSE_INTEGER_OVERFLOW; 189 return 0; 190 } 191 } 192 switch (c) { 193 case '.': case 'p': case 'P': 194 *status = PARSE_INTEGER_INVALID; 195 return 0; 196 } 197 done: 198 *value = x; 199 *status = sign; 200 return buf; 201 } 202 203 204 #define __portable_define_parse_unsigned(NAME, TYPE, LIMIT) \ 205 static inline const char *parse_ ## NAME \ 206 (const char *buf, size_t len, TYPE *value, int *status) \ 207 { \ 208 int status_ = 0; \ 209 uint64_t x; \ 210 \ 211 if (!status) { \ 212 status = &status_; \ 213 } \ 214 buf = parse_integer(buf, len, &x, status); \ 215 switch (*status) { \ 216 case PARSE_INTEGER_UNSIGNED: \ 217 if (x <= LIMIT) { \ 218 *value = (TYPE)x; \ 219 return buf; \ 220 } \ 221 *status = PARSE_INTEGER_OVERFLOW; \ 222 return 0; \ 223 case PARSE_INTEGER_SIGNED: \ 224 *status = PARSE_INTEGER_UNDERFLOW; \ 225 return 0; \ 226 default: \ 227 return buf; \ 228 } \ 229 } 230 231 #define __portable_define_parse_hex_unsigned(NAME, TYPE, LIMIT) \ 232 static inline const char *parse_hex_ ## NAME \ 233 (const char *buf, size_t len, TYPE *value, int *status) \ 234 { \ 235 int status_ = 0; \ 236 uint64_t x; \ 237 \ 238 if (!status) { \ 239 status = &status_; \ 240 } \ 241 buf = parse_hex_integer(buf, len, &x, status); \ 242 switch (*status) { \ 243 case PARSE_INTEGER_UNSIGNED: \ 244 if (x <= LIMIT) { \ 245 *value = (TYPE)x; \ 246 return buf; \ 247 } \ 248 *status = PARSE_INTEGER_OVERFLOW; \ 249 return 0; \ 250 case PARSE_INTEGER_SIGNED: \ 251 *status = PARSE_INTEGER_UNDERFLOW; \ 252 return 0; \ 253 default: \ 254 return buf; \ 255 } \ 256 } 257 258 /* This assumes two's complement. */ 259 #define __portable_define_parse_signed(NAME, TYPE, LIMIT) \ 260 static inline const char *parse_ ## NAME \ 261 (const char *buf, size_t len, TYPE *value, int *status) \ 262 { \ 263 int status_ = 0; \ 264 uint64_t x; \ 265 \ 266 if (!status) { \ 267 status = &status_; \ 268 } \ 269 buf = parse_integer(buf, len, &x, status); \ 270 switch (*status) { \ 271 case PARSE_INTEGER_UNSIGNED: \ 272 if (x <= LIMIT) { \ 273 *value = (TYPE)x; \ 274 return buf; \ 275 } \ 276 *status = PARSE_INTEGER_OVERFLOW; \ 277 return 0; \ 278 case PARSE_INTEGER_SIGNED: \ 279 if (x <= (uint64_t)(LIMIT) + 1) { \ 280 *value = (TYPE)-(int64_t)x; \ 281 return buf; \ 282 } \ 283 *status = PARSE_INTEGER_UNDERFLOW; \ 284 return 0; \ 285 default: \ 286 return buf; \ 287 } \ 288 } 289 290 /* This assumes two's complement. */ 291 #define __portable_define_parse_hex_signed(NAME, TYPE, LIMIT) \ 292 static inline const char *parse_hex_ ## NAME \ 293 (const char *buf, size_t len, TYPE *value, int *status) \ 294 { \ 295 int status_ = 0; \ 296 uint64_t x; \ 297 \ 298 if (!status) { \ 299 status = &status_; \ 300 } \ 301 buf = parse_hex_integer(buf, len, &x, status); \ 302 switch (*status) { \ 303 case PARSE_INTEGER_UNSIGNED: \ 304 if (x <= LIMIT) { \ 305 *value = (TYPE)x; \ 306 return buf; \ 307 } \ 308 *status = PARSE_INTEGER_OVERFLOW; \ 309 return 0; \ 310 case PARSE_INTEGER_SIGNED: \ 311 if (x <= (uint64_t)(LIMIT) + 1) { \ 312 *value = (TYPE)-(int64_t)x; \ 313 return buf; \ 314 } \ 315 *status = PARSE_INTEGER_UNDERFLOW; \ 316 return 0; \ 317 default: \ 318 return buf; \ 319 } \ 320 } 321 322 static inline const char *parse_uint64(const char *buf, size_t len, uint64_t *value, int *status) 323 { 324 buf = parse_integer(buf, len, value, status); 325 if (*status == PARSE_INTEGER_SIGNED) { 326 *status = PARSE_INTEGER_UNDERFLOW; 327 return 0; 328 } 329 return buf; 330 } 331 332 static inline const char *parse_hex_uint64(const char *buf, size_t len, uint64_t *value, int *status) 333 { 334 buf = parse_hex_integer(buf, len, value, status); 335 if (*status == PARSE_INTEGER_SIGNED) { 336 *status = PARSE_INTEGER_UNDERFLOW; 337 return 0; 338 } 339 return buf; 340 } 341 342 __portable_define_parse_signed(int64, int64_t, INT64_MAX) 343 __portable_define_parse_signed(int32, int32_t, INT32_MAX) 344 __portable_define_parse_unsigned(uint16, uint16_t, UINT16_MAX) 345 __portable_define_parse_signed(int16, int16_t, INT16_MAX) 346 __portable_define_parse_unsigned(uint8, uint8_t, UINT8_MAX) 347 __portable_define_parse_signed(int8, int8_t, INT8_MAX) 348 349 __portable_define_parse_hex_signed(int64, int64_t, INT64_MAX) 350 __portable_define_parse_hex_signed(int32, int32_t, INT32_MAX) 351 __portable_define_parse_hex_unsigned(uint16, uint16_t, UINT16_MAX) 352 __portable_define_parse_hex_signed(int16, int16_t, INT16_MAX) 353 __portable_define_parse_hex_unsigned(uint8, uint8_t, UINT8_MAX) 354 __portable_define_parse_hex_signed(int8, int8_t, INT8_MAX) 355 356 __portable_define_parse_unsigned(ushort, unsigned short, USHRT_MAX) 357 __portable_define_parse_signed(short, short, SHRT_MAX) 358 __portable_define_parse_unsigned(uint, unsigned int, UINT_MAX) 359 __portable_define_parse_signed(int, int, INT_MAX) 360 __portable_define_parse_unsigned(ulong, unsigned long, ULONG_MAX) 361 __portable_define_parse_signed(long, unsigned long, LONG_MAX) 362 363 __portable_define_parse_hex_unsigned(ushort, unsigned short, USHRT_MAX) 364 __portable_define_parse_hex_signed(short, short, SHRT_MAX) 365 __portable_define_parse_hex_unsigned(uint, unsigned int, UINT_MAX) 366 __portable_define_parse_hex_signed(int, int, INT_MAX) 367 __portable_define_parse_hex_unsigned(ulong, unsigned long, ULONG_MAX) 368 __portable_define_parse_hex_signed(long, unsigned long, LONG_MAX) 369 370 #ifdef __cplusplus 371 } 372 #endif 373 374 #endif /* PPARSEINT_H */