Commit 42306e2ff8716ce9a8f57da791122cc88308890c
1 parent
a66828ca
QUtil: add unsigned int/string functions
Showing
5 changed files
with
118 additions
and
34 deletions
ChangeLog
include/qpdf/QUtil.hh
| ... | ... | @@ -40,8 +40,12 @@ namespace QUtil |
| 40 | 40 | QPDF_DLL |
| 41 | 41 | std::string int_to_string(long long, int length = 0); |
| 42 | 42 | QPDF_DLL |
| 43 | + std::string uint_to_string(unsigned long long, int length = 0); | |
| 44 | + QPDF_DLL | |
| 43 | 45 | std::string int_to_string_base(long long, int base, int length = 0); |
| 44 | 46 | QPDF_DLL |
| 47 | + std::string uint_to_string_base(unsigned long long, int base, int length = 0); | |
| 48 | + QPDF_DLL | |
| 45 | 49 | std::string double_to_string(double, int decimal_places = 0); |
| 46 | 50 | |
| 47 | 51 | // These string to number methods throw std::runtime_error on |
| ... | ... | @@ -50,6 +54,10 @@ namespace QUtil |
| 50 | 54 | long long string_to_ll(char const* str); |
| 51 | 55 | QPDF_DLL |
| 52 | 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 | 62 | // Pipeline's write method wants unsigned char*, but we often have |
| 55 | 63 | // some other type of string. These methods do combinations of | ... | ... |
libqpdf/QUtil.cc
| ... | ... | @@ -9,6 +9,7 @@ |
| 9 | 9 | #include <qpdf/SecureRandomDataProvider.hh> |
| 10 | 10 | #include <qpdf/QPDFSystemError.hh> |
| 11 | 11 | #include <qpdf/QTC.hh> |
| 12 | +#include <qpdf/QIntC.hh> | |
| 12 | 13 | |
| 13 | 14 | #include <cmath> |
| 14 | 15 | #include <iomanip> |
| ... | ... | @@ -233,14 +234,10 @@ static unsigned short mac_roman_to_unicode[] = { |
| 233 | 234 | 0x02c7, // 0xff |
| 234 | 235 | }; |
| 235 | 236 | |
| 237 | +template <typename T> | |
| 238 | +static | |
| 236 | 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 | 242 | // Backward compatibility -- int_to_string, which calls this |
| 246 | 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 | 252 | buf << std::setbase(base) << std::nouppercase << num; |
| 256 | 253 | std::string result; |
| 257 | 254 | if ((length > 0) && |
| 258 | - (buf.str().length() < static_cast<size_t>(length))) | |
| 255 | + (buf.str().length() < QIntC::to_size(length))) | |
| 259 | 256 | { |
| 260 | 257 | result.append(length - buf.str().length(), '0'); |
| 261 | 258 | } |
| 262 | 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 | 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 | 265 | } |
| 269 | 266 | |
| 270 | 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 | 292 | QUtil::double_to_string(double num, int decimal_places) |
| 272 | 293 | { |
| 273 | 294 | // Backward compatibility -- this code used to use sprintf and |
| ... | ... | @@ -294,7 +315,7 @@ QUtil::string_to_ll(char const* str) |
| 294 | 315 | #endif |
| 295 | 316 | if (errno == ERANGE) |
| 296 | 317 | { |
| 297 | - throw std::runtime_error( | |
| 318 | + throw std::range_error( | |
| 298 | 319 | std::string("overflow/underflow converting ") + str |
| 299 | 320 | + " to 64-bit integer"); |
| 300 | 321 | } |
| ... | ... | @@ -304,24 +325,47 @@ QUtil::string_to_ll(char const* str) |
| 304 | 325 | int |
| 305 | 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 | 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 | 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 | 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 | 369 | unsigned char* |
| 326 | 370 | QUtil::unsigned_char_pointer(std::string const& str) |
| 327 | 371 | { |
| ... | ... | @@ -412,14 +456,18 @@ int |
| 412 | 456 | QUtil::seek(FILE* stream, qpdf_offset_t offset, int whence) |
| 413 | 457 | { |
| 414 | 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 | 462 | #elif HAVE_FSEEKO64 |
| 417 | 463 | return fseeko64(stream, offset, whence); |
| 418 | 464 | #else |
| 419 | 465 | # if defined _MSC_VER || defined __BORLANDC__ |
| 420 | 466 | return _fseeki64(stream, offset, whence); |
| 421 | 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 | 471 | # endif |
| 424 | 472 | #endif |
| 425 | 473 | } |
| ... | ... | @@ -428,14 +476,14 @@ qpdf_offset_t |
| 428 | 476 | QUtil::tell(FILE* stream) |
| 429 | 477 | { |
| 430 | 478 | #if HAVE_FSEEKO |
| 431 | - return static_cast<qpdf_offset_t>(ftello(stream)); | |
| 479 | + return QIntC::to_offset(ftello(stream)); | |
| 432 | 480 | #elif HAVE_FSEEKO64 |
| 433 | - return static_cast<qpdf_offset_t>(ftello64(stream)); | |
| 481 | + return QIntC::to_offset(ftello64(stream)); | |
| 434 | 482 | #else |
| 435 | 483 | # if defined _MSC_VER || defined __BORLANDC__ |
| 436 | 484 | return _ftelli64(stream); |
| 437 | 485 | # else |
| 438 | - return static_cast<qpdf_offset_t>(ftell(stream)); | |
| 486 | + return QIntC::to_offset(ftell(stream)); | |
| 439 | 487 | # endif |
| 440 | 488 | #endif |
| 441 | 489 | } |
| ... | ... | @@ -508,7 +556,7 @@ QUtil::hex_encode(std::string const& input) |
| 508 | 556 | for (unsigned int i = 0; i < input.length(); ++i) |
| 509 | 557 | { |
| 510 | 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 | 561 | return result; |
| 514 | 562 | } | ... | ... |
libtests/qtest/qutil/qutil.out
| ... | ... | @@ -17,12 +17,17 @@ one |
| 17 | 17 | compare okay |
| 18 | 18 | -2147483648 to int: PASSED |
| 19 | 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 | 23 | 2147483648 to int: PASSED |
| 24 | 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 | 31 | ---- os wrapper |
| 27 | 32 | before remove |
| 28 | 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 | 23 | bool threw = false; |
| 24 | 24 | bool worked = false; |
| 25 | 25 | int_T result = 0; |
| 26 | + std::string msg; | |
| 26 | 27 | try |
| 27 | 28 | { |
| 28 | 29 | result = fn(str); |
| 29 | 30 | worked = (wanted == result); |
| 30 | 31 | } |
| 31 | - catch (std::runtime_error const&) | |
| 32 | + catch (std::runtime_error const& e) | |
| 32 | 33 | { |
| 33 | 34 | threw = true; |
| 35 | + msg = e.what(); | |
| 34 | 36 | } |
| 35 | 37 | if (threw) |
| 36 | 38 | { |
| 37 | 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 | 43 | else |
| 42 | 44 | { |
| ... | ... | @@ -67,6 +69,16 @@ void test_to_ll(char const* str, long long wanted, bool error) |
| 67 | 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 | 82 | void string_conversion_test() |
| 71 | 83 | { |
| 72 | 84 | std::cout << QUtil::int_to_string(16059) << std::endl |
| ... | ... | @@ -105,6 +117,8 @@ void string_conversion_test() |
| 105 | 117 | long long int_min_minus_1 = static_cast<long long>(INT_MIN) - 1; |
| 106 | 118 | std::string int_max_plus_1_str = QUtil::int_to_string(int_max_plus_1); |
| 107 | 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 | 122 | test_to_int(int_min_str.c_str(), INT_MIN, false); |
| 109 | 123 | test_to_int(int_max_str.c_str(), INT_MAX, false); |
| 110 | 124 | test_to_int(int_max_plus_1_str.c_str(), 0, true); |
| ... | ... | @@ -113,6 +127,11 @@ void string_conversion_test() |
| 113 | 127 | test_to_ll(int_max_plus_1_str.c_str(), int_max_plus_1, false); |
| 114 | 128 | test_to_ll(int_min_minus_1_str.c_str(), int_min_minus_1, false); |
| 115 | 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 | 137 | void os_wrapper_test() |
| ... | ... | @@ -159,7 +178,7 @@ void getenv_test() |
| 159 | 178 | static void print_utf8(unsigned long val) |
| 160 | 179 | { |
| 161 | 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 | 182 | if (val < 0xfffe) |
| 164 | 183 | { |
| 165 | 184 | std::cout << " " << result; |
| ... | ... | @@ -199,7 +218,7 @@ void to_utf8_test() |
| 199 | 218 | static void print_utf16(unsigned long val) |
| 200 | 219 | { |
| 201 | 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 | 222 | for (std::string::iterator iter = result.begin(); |
| 204 | 223 | iter != result.end(); ++iter) |
| 205 | 224 | { |
| ... | ... | @@ -249,7 +268,7 @@ void transcoding_test(std::string (*to_utf8)(std::string const&), |
| 249 | 268 | std::string back; |
| 250 | 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 | 272 | out = (*to_utf8)(in); |
| 254 | 273 | std::string wanted = (out == "\xef\xbf\xbd") ? unknown : in; |
| 255 | 274 | back = (*from_utf8)(out, '?'); | ... | ... |