Commit 42306e2ff8716ce9a8f57da791122cc88308890c
1 parent
a66828ca
QUtil: add unsigned int/string functions
Showing
5 changed files
with
118 additions
and
34 deletions
ChangeLog
| 1 | 2019-06-20 Jay Berkenbilt <ejb@ql.org> | 1 | 2019-06-20 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | ||
| 3 | + * Add functions to QUtil to convert unsigned integers to strings, | ||
| 4 | + avoiding implicit conversion between unsigned and signed integer | ||
| 5 | + types. | ||
| 6 | + | ||
| 3 | * Add QIC.hh, containing integer type converters that do range | 7 | * Add QIC.hh, containing integer type converters that do range |
| 4 | checking. | 8 | checking. |
| 5 | 9 |
include/qpdf/QUtil.hh
| @@ -40,8 +40,12 @@ namespace QUtil | @@ -40,8 +40,12 @@ namespace QUtil | ||
| 40 | QPDF_DLL | 40 | QPDF_DLL |
| 41 | std::string int_to_string(long long, int length = 0); | 41 | std::string int_to_string(long long, int length = 0); |
| 42 | QPDF_DLL | 42 | QPDF_DLL |
| 43 | + std::string uint_to_string(unsigned long long, int length = 0); | ||
| 44 | + QPDF_DLL | ||
| 43 | std::string int_to_string_base(long long, int base, int length = 0); | 45 | std::string int_to_string_base(long long, int base, int length = 0); |
| 44 | QPDF_DLL | 46 | QPDF_DLL |
| 47 | + std::string uint_to_string_base(unsigned long long, int base, int length = 0); | ||
| 48 | + QPDF_DLL | ||
| 45 | std::string double_to_string(double, int decimal_places = 0); | 49 | std::string double_to_string(double, int decimal_places = 0); |
| 46 | 50 | ||
| 47 | // These string to number methods throw std::runtime_error on | 51 | // These string to number methods throw std::runtime_error on |
| @@ -50,6 +54,10 @@ namespace QUtil | @@ -50,6 +54,10 @@ namespace QUtil | ||
| 50 | long long string_to_ll(char const* str); | 54 | long long string_to_ll(char const* str); |
| 51 | QPDF_DLL | 55 | QPDF_DLL |
| 52 | int string_to_int(char const* str); | 56 | int string_to_int(char const* str); |
| 57 | + QPDF_DLL | ||
| 58 | + unsigned long long string_to_ull(char const* str); | ||
| 59 | + QPDF_DLL | ||
| 60 | + unsigned int string_to_uint(char const* str); | ||
| 53 | 61 | ||
| 54 | // Pipeline's write method wants unsigned char*, but we often have | 62 | // Pipeline's write method wants unsigned char*, but we often have |
| 55 | // some other type of string. These methods do combinations of | 63 | // some other type of string. These methods do combinations of |
libqpdf/QUtil.cc
| @@ -9,6 +9,7 @@ | @@ -9,6 +9,7 @@ | ||
| 9 | #include <qpdf/SecureRandomDataProvider.hh> | 9 | #include <qpdf/SecureRandomDataProvider.hh> |
| 10 | #include <qpdf/QPDFSystemError.hh> | 10 | #include <qpdf/QPDFSystemError.hh> |
| 11 | #include <qpdf/QTC.hh> | 11 | #include <qpdf/QTC.hh> |
| 12 | +#include <qpdf/QIntC.hh> | ||
| 12 | 13 | ||
| 13 | #include <cmath> | 14 | #include <cmath> |
| 14 | #include <iomanip> | 15 | #include <iomanip> |
| @@ -233,14 +234,10 @@ static unsigned short mac_roman_to_unicode[] = { | @@ -233,14 +234,10 @@ static unsigned short mac_roman_to_unicode[] = { | ||
| 233 | 0x02c7, // 0xff | 234 | 0x02c7, // 0xff |
| 234 | }; | 235 | }; |
| 235 | 236 | ||
| 237 | +template <typename T> | ||
| 238 | +static | ||
| 236 | std::string | 239 | std::string |
| 237 | -QUtil::int_to_string(long long num, int length) | ||
| 238 | -{ | ||
| 239 | - return int_to_string_base(num, 10, length); | ||
| 240 | -} | ||
| 241 | - | ||
| 242 | -std::string | ||
| 243 | -QUtil::int_to_string_base(long long num, int base, int length) | 240 | +int_to_string_base_internal(T num, int base, int length) |
| 244 | { | 241 | { |
| 245 | // Backward compatibility -- int_to_string, which calls this | 242 | // Backward compatibility -- int_to_string, which calls this |
| 246 | // function, used to use sprintf with %0*d, so we interpret length | 243 | // function, used to use sprintf with %0*d, so we interpret length |
| @@ -255,12 +252,12 @@ QUtil::int_to_string_base(long long num, int base, int length) | @@ -255,12 +252,12 @@ QUtil::int_to_string_base(long long num, int base, int length) | ||
| 255 | buf << std::setbase(base) << std::nouppercase << num; | 252 | buf << std::setbase(base) << std::nouppercase << num; |
| 256 | std::string result; | 253 | std::string result; |
| 257 | if ((length > 0) && | 254 | if ((length > 0) && |
| 258 | - (buf.str().length() < static_cast<size_t>(length))) | 255 | + (buf.str().length() < QIntC::to_size(length))) |
| 259 | { | 256 | { |
| 260 | result.append(length - buf.str().length(), '0'); | 257 | result.append(length - buf.str().length(), '0'); |
| 261 | } | 258 | } |
| 262 | result += buf.str(); | 259 | result += buf.str(); |
| 263 | - if ((length < 0) && (buf.str().length() < static_cast<size_t>(-length))) | 260 | + if ((length < 0) && (buf.str().length() < QIntC::to_size(-length))) |
| 264 | { | 261 | { |
| 265 | result.append(-length - buf.str().length(), ' '); | 262 | result.append(-length - buf.str().length(), ' '); |
| 266 | } | 263 | } |
| @@ -268,6 +265,30 @@ QUtil::int_to_string_base(long long num, int base, int length) | @@ -268,6 +265,30 @@ QUtil::int_to_string_base(long long num, int base, int length) | ||
| 268 | } | 265 | } |
| 269 | 266 | ||
| 270 | std::string | 267 | std::string |
| 268 | +QUtil::int_to_string(long long num, int length) | ||
| 269 | +{ | ||
| 270 | + return int_to_string_base(num, 10, length); | ||
| 271 | +} | ||
| 272 | + | ||
| 273 | +std::string | ||
| 274 | +QUtil::uint_to_string(unsigned long long num, int length) | ||
| 275 | +{ | ||
| 276 | + return int_to_string_base(num, 10, length); | ||
| 277 | +} | ||
| 278 | + | ||
| 279 | +std::string | ||
| 280 | +QUtil::int_to_string_base(long long num, int base, int length) | ||
| 281 | +{ | ||
| 282 | + return int_to_string_base_internal(num, base, length); | ||
| 283 | +} | ||
| 284 | + | ||
| 285 | +std::string | ||
| 286 | +QUtil::uint_to_string_base(unsigned long long num, int base, int length) | ||
| 287 | +{ | ||
| 288 | + return int_to_string_base_internal(num, base, length); | ||
| 289 | +} | ||
| 290 | + | ||
| 291 | +std::string | ||
| 271 | QUtil::double_to_string(double num, int decimal_places) | 292 | QUtil::double_to_string(double num, int decimal_places) |
| 272 | { | 293 | { |
| 273 | // Backward compatibility -- this code used to use sprintf and | 294 | // Backward compatibility -- this code used to use sprintf and |
| @@ -294,7 +315,7 @@ QUtil::string_to_ll(char const* str) | @@ -294,7 +315,7 @@ QUtil::string_to_ll(char const* str) | ||
| 294 | #endif | 315 | #endif |
| 295 | if (errno == ERANGE) | 316 | if (errno == ERANGE) |
| 296 | { | 317 | { |
| 297 | - throw std::runtime_error( | 318 | + throw std::range_error( |
| 298 | std::string("overflow/underflow converting ") + str | 319 | std::string("overflow/underflow converting ") + str |
| 299 | + " to 64-bit integer"); | 320 | + " to 64-bit integer"); |
| 300 | } | 321 | } |
| @@ -304,24 +325,47 @@ QUtil::string_to_ll(char const* str) | @@ -304,24 +325,47 @@ QUtil::string_to_ll(char const* str) | ||
| 304 | int | 325 | int |
| 305 | QUtil::string_to_int(char const* str) | 326 | QUtil::string_to_int(char const* str) |
| 306 | { | 327 | { |
| 307 | - errno = 0; | ||
| 308 | - long long_val = strtol(str, 0, 10); | ||
| 309 | - if (errno == ERANGE) | 328 | + // QIntC::to_int does range checking |
| 329 | + return QIntC::to_int(string_to_ll(str)); | ||
| 330 | +} | ||
| 331 | + | ||
| 332 | +unsigned long long | ||
| 333 | +QUtil::string_to_ull(char const* str) | ||
| 334 | +{ | ||
| 335 | + char const* p = str; | ||
| 336 | + while (*p && is_space(*p)) | ||
| 337 | + { | ||
| 338 | + ++p; | ||
| 339 | + } | ||
| 340 | + if (*p == '-') | ||
| 310 | { | 341 | { |
| 311 | throw std::runtime_error( | 342 | throw std::runtime_error( |
| 312 | - std::string("overflow/underflow converting ") + str | ||
| 313 | - + " to long integer"); | 343 | + std::string("underflow converting ") + str |
| 344 | + + " to 64-bit unsigned integer"); | ||
| 314 | } | 345 | } |
| 315 | - int result = static_cast<int>(long_val); | ||
| 316 | - if (static_cast<long>(result) != long_val) | 346 | + |
| 347 | + errno = 0; | ||
| 348 | +#ifdef _MSC_VER | ||
| 349 | + unsigned long long result = _strtoui64(str, 0, 10); | ||
| 350 | +#else | ||
| 351 | + unsigned long long result = strtoull(str, 0, 10); | ||
| 352 | +#endif | ||
| 353 | + if (errno == ERANGE) | ||
| 317 | { | 354 | { |
| 318 | throw std::runtime_error( | 355 | throw std::runtime_error( |
| 319 | - std::string("overflow/underflow converting ") + str | ||
| 320 | - + " to integer"); | 356 | + std::string("overflow converting ") + str |
| 357 | + + " to 64-bit unsigned integer"); | ||
| 321 | } | 358 | } |
| 322 | return result; | 359 | return result; |
| 323 | } | 360 | } |
| 324 | 361 | ||
| 362 | +unsigned int | ||
| 363 | +QUtil::string_to_uint(char const* str) | ||
| 364 | +{ | ||
| 365 | + // QIntC::to_uint does range checking | ||
| 366 | + return QIntC::to_uint(string_to_ull(str)); | ||
| 367 | +} | ||
| 368 | + | ||
| 325 | unsigned char* | 369 | unsigned char* |
| 326 | QUtil::unsigned_char_pointer(std::string const& str) | 370 | QUtil::unsigned_char_pointer(std::string const& str) |
| 327 | { | 371 | { |
| @@ -412,14 +456,18 @@ int | @@ -412,14 +456,18 @@ int | ||
| 412 | QUtil::seek(FILE* stream, qpdf_offset_t offset, int whence) | 456 | QUtil::seek(FILE* stream, qpdf_offset_t offset, int whence) |
| 413 | { | 457 | { |
| 414 | #if HAVE_FSEEKO | 458 | #if HAVE_FSEEKO |
| 415 | - return fseeko(stream, static_cast<off_t>(offset), whence); | 459 | + return fseeko(stream, |
| 460 | + QIntC::IntConverter<qpdf_offset_t, off_t>::convert(offset), | ||
| 461 | + whence); | ||
| 416 | #elif HAVE_FSEEKO64 | 462 | #elif HAVE_FSEEKO64 |
| 417 | return fseeko64(stream, offset, whence); | 463 | return fseeko64(stream, offset, whence); |
| 418 | #else | 464 | #else |
| 419 | # if defined _MSC_VER || defined __BORLANDC__ | 465 | # if defined _MSC_VER || defined __BORLANDC__ |
| 420 | return _fseeki64(stream, offset, whence); | 466 | return _fseeki64(stream, offset, whence); |
| 421 | # else | 467 | # else |
| 422 | - return fseek(stream, static_cast<long>(offset), whence); | 468 | + return fseek(stream, |
| 469 | + QIntC::IntConverter<qpdf_offset_t, long>(offset), | ||
| 470 | + whence); | ||
| 423 | # endif | 471 | # endif |
| 424 | #endif | 472 | #endif |
| 425 | } | 473 | } |
| @@ -428,14 +476,14 @@ qpdf_offset_t | @@ -428,14 +476,14 @@ qpdf_offset_t | ||
| 428 | QUtil::tell(FILE* stream) | 476 | QUtil::tell(FILE* stream) |
| 429 | { | 477 | { |
| 430 | #if HAVE_FSEEKO | 478 | #if HAVE_FSEEKO |
| 431 | - return static_cast<qpdf_offset_t>(ftello(stream)); | 479 | + return QIntC::to_offset(ftello(stream)); |
| 432 | #elif HAVE_FSEEKO64 | 480 | #elif HAVE_FSEEKO64 |
| 433 | - return static_cast<qpdf_offset_t>(ftello64(stream)); | 481 | + return QIntC::to_offset(ftello64(stream)); |
| 434 | #else | 482 | #else |
| 435 | # if defined _MSC_VER || defined __BORLANDC__ | 483 | # if defined _MSC_VER || defined __BORLANDC__ |
| 436 | return _ftelli64(stream); | 484 | return _ftelli64(stream); |
| 437 | # else | 485 | # else |
| 438 | - return static_cast<qpdf_offset_t>(ftell(stream)); | 486 | + return QIntC::to_offset(ftell(stream)); |
| 439 | # endif | 487 | # endif |
| 440 | #endif | 488 | #endif |
| 441 | } | 489 | } |
| @@ -508,7 +556,7 @@ QUtil::hex_encode(std::string const& input) | @@ -508,7 +556,7 @@ QUtil::hex_encode(std::string const& input) | ||
| 508 | for (unsigned int i = 0; i < input.length(); ++i) | 556 | for (unsigned int i = 0; i < input.length(); ++i) |
| 509 | { | 557 | { |
| 510 | result += QUtil::int_to_string_base( | 558 | result += QUtil::int_to_string_base( |
| 511 | - static_cast<int>(static_cast<unsigned char>(input.at(i))), 16, 2); | 559 | + QIntC::to_int(static_cast<unsigned char>(input.at(i))), 16, 2); |
| 512 | } | 560 | } |
| 513 | return result; | 561 | return result; |
| 514 | } | 562 | } |
libtests/qtest/qutil/qutil.out
| @@ -17,12 +17,17 @@ one | @@ -17,12 +17,17 @@ one | ||
| 17 | compare okay | 17 | compare okay |
| 18 | -2147483648 to int: PASSED | 18 | -2147483648 to int: PASSED |
| 19 | 2147483647 to int: PASSED | 19 | 2147483647 to int: PASSED |
| 20 | -2147483648 to int threw: PASSED | ||
| 21 | --2147483649 to int threw: PASSED | ||
| 22 | -9999999999999999999999999 to int threw: PASSED | 20 | +2147483648 to int threw (integer out of range converting 2147483648 from a 8-byte signed type to a 4-byte signed type): PASSED |
| 21 | +-2147483649 to int threw (integer out of range converting -2147483649 from a 8-byte signed type to a 4-byte signed type): PASSED | ||
| 22 | +9999999999999999999999999 to int threw (overflow/underflow converting 9999999999999999999999999 to 64-bit integer): PASSED | ||
| 23 | 2147483648 to int: PASSED | 23 | 2147483648 to int: PASSED |
| 24 | -2147483649 to int: PASSED | 24 | -2147483649 to int: PASSED |
| 25 | -99999999999999999999999999999999999999999999999999 to int threw: PASSED | 25 | +99999999999999999999999999999999999999999999999999 to int threw (overflow/underflow converting 99999999999999999999999999999999999999999999999999 to 64-bit integer): PASSED |
| 26 | +16059 to int: PASSED | ||
| 27 | +-16059 to int threw (underflow converting -16059 to 64-bit unsigned integer): PASSED | ||
| 28 | +9999999999 to int threw (integer out of range converting 9999999999 from a 8-byte unsigned type to a 4-byte unsigned type): PASSED | ||
| 29 | +16059 to int: PASSED | ||
| 30 | +-16059 to int threw (underflow converting -16059 to 64-bit unsigned integer): PASSED | ||
| 26 | ---- os wrapper | 31 | ---- os wrapper |
| 27 | before remove | 32 | before remove |
| 28 | exception: remove file: No such file or directory | 33 | exception: remove file: No such file or directory |
libtests/qutil.cc
| @@ -23,20 +23,22 @@ void test_to_number(char const* str, int_T wanted, bool error, | @@ -23,20 +23,22 @@ void test_to_number(char const* str, int_T wanted, bool error, | ||
| 23 | bool threw = false; | 23 | bool threw = false; |
| 24 | bool worked = false; | 24 | bool worked = false; |
| 25 | int_T result = 0; | 25 | int_T result = 0; |
| 26 | + std::string msg; | ||
| 26 | try | 27 | try |
| 27 | { | 28 | { |
| 28 | result = fn(str); | 29 | result = fn(str); |
| 29 | worked = (wanted == result); | 30 | worked = (wanted == result); |
| 30 | } | 31 | } |
| 31 | - catch (std::runtime_error const&) | 32 | + catch (std::runtime_error const& e) |
| 32 | { | 33 | { |
| 33 | threw = true; | 34 | threw = true; |
| 35 | + msg = e.what(); | ||
| 34 | } | 36 | } |
| 35 | if (threw) | 37 | if (threw) |
| 36 | { | 38 | { |
| 37 | if (error) | 39 | if (error) |
| 38 | { | 40 | { |
| 39 | - std::cout << str << " to int threw: PASSED" << std::endl; | 41 | + std::cout << str << " to int threw (" << msg << "): PASSED" << std::endl; |
| 40 | } | 42 | } |
| 41 | else | 43 | else |
| 42 | { | 44 | { |
| @@ -67,6 +69,16 @@ void test_to_ll(char const* str, long long wanted, bool error) | @@ -67,6 +69,16 @@ void test_to_ll(char const* str, long long wanted, bool error) | ||
| 67 | test_to_number(str, wanted, error, QUtil::string_to_ll); | 69 | test_to_number(str, wanted, error, QUtil::string_to_ll); |
| 68 | } | 70 | } |
| 69 | 71 | ||
| 72 | +void test_to_uint(char const* str, unsigned int wanted, bool error) | ||
| 73 | +{ | ||
| 74 | + test_to_number(str, wanted, error, QUtil::string_to_uint); | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +void test_to_ull(char const* str, unsigned long long wanted, bool error) | ||
| 78 | +{ | ||
| 79 | + test_to_number(str, wanted, error, QUtil::string_to_ull); | ||
| 80 | +} | ||
| 81 | + | ||
| 70 | void string_conversion_test() | 82 | void string_conversion_test() |
| 71 | { | 83 | { |
| 72 | std::cout << QUtil::int_to_string(16059) << std::endl | 84 | std::cout << QUtil::int_to_string(16059) << std::endl |
| @@ -105,6 +117,8 @@ void string_conversion_test() | @@ -105,6 +117,8 @@ void string_conversion_test() | ||
| 105 | long long int_min_minus_1 = static_cast<long long>(INT_MIN) - 1; | 117 | long long int_min_minus_1 = static_cast<long long>(INT_MIN) - 1; |
| 106 | std::string int_max_plus_1_str = QUtil::int_to_string(int_max_plus_1); | 118 | std::string int_max_plus_1_str = QUtil::int_to_string(int_max_plus_1); |
| 107 | std::string int_min_minus_1_str = QUtil::int_to_string(int_min_minus_1); | 119 | std::string int_min_minus_1_str = QUtil::int_to_string(int_min_minus_1); |
| 120 | + std::string small_positive = QUtil::uint_to_string(16059U); | ||
| 121 | + std::string small_negative = QUtil::int_to_string(-16059); | ||
| 108 | test_to_int(int_min_str.c_str(), INT_MIN, false); | 122 | test_to_int(int_min_str.c_str(), INT_MIN, false); |
| 109 | test_to_int(int_max_str.c_str(), INT_MAX, false); | 123 | test_to_int(int_max_str.c_str(), INT_MAX, false); |
| 110 | test_to_int(int_max_plus_1_str.c_str(), 0, true); | 124 | test_to_int(int_max_plus_1_str.c_str(), 0, true); |
| @@ -113,6 +127,11 @@ void string_conversion_test() | @@ -113,6 +127,11 @@ void string_conversion_test() | ||
| 113 | test_to_ll(int_max_plus_1_str.c_str(), int_max_plus_1, false); | 127 | test_to_ll(int_max_plus_1_str.c_str(), int_max_plus_1, false); |
| 114 | test_to_ll(int_min_minus_1_str.c_str(), int_min_minus_1, false); | 128 | test_to_ll(int_min_minus_1_str.c_str(), int_min_minus_1, false); |
| 115 | test_to_ll("99999999999999999999999999999999999999999999999999", 0, true); | 129 | test_to_ll("99999999999999999999999999999999999999999999999999", 0, true); |
| 130 | + test_to_uint(small_positive.c_str(), 16059U, false); | ||
| 131 | + test_to_uint(small_negative.c_str(), 0, true); | ||
| 132 | + test_to_uint("9999999999", 0, true); | ||
| 133 | + test_to_ull(small_positive.c_str(), 16059U, false); | ||
| 134 | + test_to_ull(small_negative.c_str(), 0, true); | ||
| 116 | } | 135 | } |
| 117 | 136 | ||
| 118 | void os_wrapper_test() | 137 | void os_wrapper_test() |
| @@ -159,7 +178,7 @@ void getenv_test() | @@ -159,7 +178,7 @@ void getenv_test() | ||
| 159 | static void print_utf8(unsigned long val) | 178 | static void print_utf8(unsigned long val) |
| 160 | { | 179 | { |
| 161 | std::string result = QUtil::toUTF8(val); | 180 | std::string result = QUtil::toUTF8(val); |
| 162 | - std::cout << "0x" << QUtil::int_to_string_base(val, 16) << " ->"; | 181 | + std::cout << "0x" << QUtil::uint_to_string_base(val, 16) << " ->"; |
| 163 | if (val < 0xfffe) | 182 | if (val < 0xfffe) |
| 164 | { | 183 | { |
| 165 | std::cout << " " << result; | 184 | std::cout << " " << result; |
| @@ -199,7 +218,7 @@ void to_utf8_test() | @@ -199,7 +218,7 @@ void to_utf8_test() | ||
| 199 | static void print_utf16(unsigned long val) | 218 | static void print_utf16(unsigned long val) |
| 200 | { | 219 | { |
| 201 | std::string result = QUtil::toUTF16(val); | 220 | std::string result = QUtil::toUTF16(val); |
| 202 | - std::cout << "0x" << QUtil::int_to_string_base(val, 16) << " ->"; | 221 | + std::cout << "0x" << QUtil::uint_to_string_base(val, 16) << " ->"; |
| 203 | for (std::string::iterator iter = result.begin(); | 222 | for (std::string::iterator iter = result.begin(); |
| 204 | iter != result.end(); ++iter) | 223 | iter != result.end(); ++iter) |
| 205 | { | 224 | { |
| @@ -249,7 +268,7 @@ void transcoding_test(std::string (*to_utf8)(std::string const&), | @@ -249,7 +268,7 @@ void transcoding_test(std::string (*to_utf8)(std::string const&), | ||
| 249 | std::string back; | 268 | std::string back; |
| 250 | for (int i = 128; i <= last; ++i) | 269 | for (int i = 128; i <= last; ++i) |
| 251 | { | 270 | { |
| 252 | - in.at(0) = static_cast<unsigned char>(i); | 271 | + in.at(0) = static_cast<char>(static_cast<unsigned char>(i)); |
| 253 | out = (*to_utf8)(in); | 272 | out = (*to_utf8)(in); |
| 254 | std::string wanted = (out == "\xef\xbf\xbd") ? unknown : in; | 273 | std::string wanted = (out == "\xef\xbf\xbd") ? unknown : in; |
| 255 | back = (*from_utf8)(out, '?'); | 274 | back = (*from_utf8)(out, '?'); |