Commit 42306e2ff8716ce9a8f57da791122cc88308890c

Authored by Jay Berkenbilt
1 parent a66828ca

QUtil: add unsigned int/string functions

ChangeLog
1 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 7 * Add QIC.hh, containing integer type converters that do range
4 8 checking.
5 9  
... ...
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&amp; 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&amp;),
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, '?');
... ...