Commit 42306e2ff8716ce9a8f57da791122cc88308890c

Authored by Jay Berkenbilt
1 parent a66828ca

QUtil: add unsigned int/string functions

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&amp; input) @@ -508,7 +556,7 @@ QUtil::hex_encode(std::string const&amp; 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&amp;), @@ -249,7 +268,7 @@ void transcoding_test(std::string (*to_utf8)(std::string const&amp;),
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, '?');