aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2019-06-21 01:48:53 +0200
committerJay Berkenbilt <ejb@ql.org>2019-06-21 19:17:21 +0200
commit42306e2ff8716ce9a8f57da791122cc88308890c (patch)
tree9050f3ae8a9cab1524ab41df0b7b3f683b974870
parenta66828caff16a4ad64b9d69b5db1c5a5e60418cc (diff)
downloadqpdf-42306e2ff8716ce9a8f57da791122cc88308890c.tar.zst
QUtil: add unsigned int/string functions
-rw-r--r--ChangeLog4
-rw-r--r--include/qpdf/QUtil.hh8
-rw-r--r--libqpdf/QUtil.cc98
-rw-r--r--libtests/qtest/qutil/qutil.out13
-rw-r--r--libtests/qutil.cc29
5 files changed, 118 insertions, 34 deletions
diff --git a/ChangeLog b/ChangeLog
index d88fad35..d51a92c5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
2019-06-20 Jay Berkenbilt <ejb@ql.org>
+ * Add functions to QUtil to convert unsigned integers to strings,
+ avoiding implicit conversion between unsigned and signed integer
+ types.
+
* Add QIC.hh, containing integer type converters that do range
checking.
diff --git a/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh
index 02dec5ad..afdd2033 100644
--- a/include/qpdf/QUtil.hh
+++ b/include/qpdf/QUtil.hh
@@ -40,8 +40,12 @@ namespace QUtil
QPDF_DLL
std::string int_to_string(long long, int length = 0);
QPDF_DLL
+ std::string uint_to_string(unsigned long long, int length = 0);
+ QPDF_DLL
std::string int_to_string_base(long long, int base, int length = 0);
QPDF_DLL
+ std::string uint_to_string_base(unsigned long long, int base, int length = 0);
+ QPDF_DLL
std::string double_to_string(double, int decimal_places = 0);
// These string to number methods throw std::runtime_error on
@@ -50,6 +54,10 @@ namespace QUtil
long long string_to_ll(char const* str);
QPDF_DLL
int string_to_int(char const* str);
+ QPDF_DLL
+ unsigned long long string_to_ull(char const* str);
+ QPDF_DLL
+ unsigned int string_to_uint(char const* str);
// Pipeline's write method wants unsigned char*, but we often have
// some other type of string. These methods do combinations of
diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc
index 17e958ca..dee3a636 100644
--- a/libqpdf/QUtil.cc
+++ b/libqpdf/QUtil.cc
@@ -9,6 +9,7 @@
#include <qpdf/SecureRandomDataProvider.hh>
#include <qpdf/QPDFSystemError.hh>
#include <qpdf/QTC.hh>
+#include <qpdf/QIntC.hh>
#include <cmath>
#include <iomanip>
@@ -233,14 +234,10 @@ static unsigned short mac_roman_to_unicode[] = {
0x02c7, // 0xff
};
+template <typename T>
+static
std::string
-QUtil::int_to_string(long long num, int length)
-{
- return int_to_string_base(num, 10, length);
-}
-
-std::string
-QUtil::int_to_string_base(long long num, int base, int length)
+int_to_string_base_internal(T num, int base, int length)
{
// Backward compatibility -- int_to_string, which calls this
// 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)
buf << std::setbase(base) << std::nouppercase << num;
std::string result;
if ((length > 0) &&
- (buf.str().length() < static_cast<size_t>(length)))
+ (buf.str().length() < QIntC::to_size(length)))
{
result.append(length - buf.str().length(), '0');
}
result += buf.str();
- if ((length < 0) && (buf.str().length() < static_cast<size_t>(-length)))
+ if ((length < 0) && (buf.str().length() < QIntC::to_size(-length)))
{
result.append(-length - buf.str().length(), ' ');
}
@@ -268,6 +265,30 @@ QUtil::int_to_string_base(long long num, int base, int length)
}
std::string
+QUtil::int_to_string(long long num, int length)
+{
+ return int_to_string_base(num, 10, length);
+}
+
+std::string
+QUtil::uint_to_string(unsigned long long num, int length)
+{
+ return int_to_string_base(num, 10, length);
+}
+
+std::string
+QUtil::int_to_string_base(long long num, int base, int length)
+{
+ return int_to_string_base_internal(num, base, length);
+}
+
+std::string
+QUtil::uint_to_string_base(unsigned long long num, int base, int length)
+{
+ return int_to_string_base_internal(num, base, length);
+}
+
+std::string
QUtil::double_to_string(double num, int decimal_places)
{
// Backward compatibility -- this code used to use sprintf and
@@ -294,7 +315,7 @@ QUtil::string_to_ll(char const* str)
#endif
if (errno == ERANGE)
{
- throw std::runtime_error(
+ throw std::range_error(
std::string("overflow/underflow converting ") + str
+ " to 64-bit integer");
}
@@ -304,24 +325,47 @@ QUtil::string_to_ll(char const* str)
int
QUtil::string_to_int(char const* str)
{
- errno = 0;
- long long_val = strtol(str, 0, 10);
- if (errno == ERANGE)
+ // QIntC::to_int does range checking
+ return QIntC::to_int(string_to_ll(str));
+}
+
+unsigned long long
+QUtil::string_to_ull(char const* str)
+{
+ char const* p = str;
+ while (*p && is_space(*p))
+ {
+ ++p;
+ }
+ if (*p == '-')
{
throw std::runtime_error(
- std::string("overflow/underflow converting ") + str
- + " to long integer");
+ std::string("underflow converting ") + str
+ + " to 64-bit unsigned integer");
}
- int result = static_cast<int>(long_val);
- if (static_cast<long>(result) != long_val)
+
+ errno = 0;
+#ifdef _MSC_VER
+ unsigned long long result = _strtoui64(str, 0, 10);
+#else
+ unsigned long long result = strtoull(str, 0, 10);
+#endif
+ if (errno == ERANGE)
{
throw std::runtime_error(
- std::string("overflow/underflow converting ") + str
- + " to integer");
+ std::string("overflow converting ") + str
+ + " to 64-bit unsigned integer");
}
return result;
}
+unsigned int
+QUtil::string_to_uint(char const* str)
+{
+ // QIntC::to_uint does range checking
+ return QIntC::to_uint(string_to_ull(str));
+}
+
unsigned char*
QUtil::unsigned_char_pointer(std::string const& str)
{
@@ -412,14 +456,18 @@ int
QUtil::seek(FILE* stream, qpdf_offset_t offset, int whence)
{
#if HAVE_FSEEKO
- return fseeko(stream, static_cast<off_t>(offset), whence);
+ return fseeko(stream,
+ QIntC::IntConverter<qpdf_offset_t, off_t>::convert(offset),
+ whence);
#elif HAVE_FSEEKO64
return fseeko64(stream, offset, whence);
#else
# if defined _MSC_VER || defined __BORLANDC__
return _fseeki64(stream, offset, whence);
# else
- return fseek(stream, static_cast<long>(offset), whence);
+ return fseek(stream,
+ QIntC::IntConverter<qpdf_offset_t, long>(offset),
+ whence);
# endif
#endif
}
@@ -428,14 +476,14 @@ qpdf_offset_t
QUtil::tell(FILE* stream)
{
#if HAVE_FSEEKO
- return static_cast<qpdf_offset_t>(ftello(stream));
+ return QIntC::to_offset(ftello(stream));
#elif HAVE_FSEEKO64
- return static_cast<qpdf_offset_t>(ftello64(stream));
+ return QIntC::to_offset(ftello64(stream));
#else
# if defined _MSC_VER || defined __BORLANDC__
return _ftelli64(stream);
# else
- return static_cast<qpdf_offset_t>(ftell(stream));
+ return QIntC::to_offset(ftell(stream));
# endif
#endif
}
@@ -508,7 +556,7 @@ QUtil::hex_encode(std::string const& input)
for (unsigned int i = 0; i < input.length(); ++i)
{
result += QUtil::int_to_string_base(
- static_cast<int>(static_cast<unsigned char>(input.at(i))), 16, 2);
+ QIntC::to_int(static_cast<unsigned char>(input.at(i))), 16, 2);
}
return result;
}
diff --git a/libtests/qtest/qutil/qutil.out b/libtests/qtest/qutil/qutil.out
index c35f22e3..4d19617d 100644
--- a/libtests/qtest/qutil/qutil.out
+++ b/libtests/qtest/qutil/qutil.out
@@ -17,12 +17,17 @@ one
compare okay
-2147483648 to int: PASSED
2147483647 to int: PASSED
-2147483648 to int threw: PASSED
--2147483649 to int threw: PASSED
-9999999999999999999999999 to int threw: PASSED
+2147483648 to int threw (integer out of range converting 2147483648 from a 8-byte signed type to a 4-byte signed type): PASSED
+-2147483649 to int threw (integer out of range converting -2147483649 from a 8-byte signed type to a 4-byte signed type): PASSED
+9999999999999999999999999 to int threw (overflow/underflow converting 9999999999999999999999999 to 64-bit integer): PASSED
2147483648 to int: PASSED
-2147483649 to int: PASSED
-99999999999999999999999999999999999999999999999999 to int threw: PASSED
+99999999999999999999999999999999999999999999999999 to int threw (overflow/underflow converting 99999999999999999999999999999999999999999999999999 to 64-bit integer): PASSED
+16059 to int: PASSED
+-16059 to int threw (underflow converting -16059 to 64-bit unsigned integer): PASSED
+9999999999 to int threw (integer out of range converting 9999999999 from a 8-byte unsigned type to a 4-byte unsigned type): PASSED
+16059 to int: PASSED
+-16059 to int threw (underflow converting -16059 to 64-bit unsigned integer): PASSED
---- os wrapper
before remove
exception: remove file: No such file or directory
diff --git a/libtests/qutil.cc b/libtests/qutil.cc
index 27881c6e..0e0a063b 100644
--- a/libtests/qutil.cc
+++ b/libtests/qutil.cc
@@ -23,20 +23,22 @@ void test_to_number(char const* str, int_T wanted, bool error,
bool threw = false;
bool worked = false;
int_T result = 0;
+ std::string msg;
try
{
result = fn(str);
worked = (wanted == result);
}
- catch (std::runtime_error const&)
+ catch (std::runtime_error const& e)
{
threw = true;
+ msg = e.what();
}
if (threw)
{
if (error)
{
- std::cout << str << " to int threw: PASSED" << std::endl;
+ std::cout << str << " to int threw (" << msg << "): PASSED" << std::endl;
}
else
{
@@ -67,6 +69,16 @@ void test_to_ll(char const* str, long long wanted, bool error)
test_to_number(str, wanted, error, QUtil::string_to_ll);
}
+void test_to_uint(char const* str, unsigned int wanted, bool error)
+{
+ test_to_number(str, wanted, error, QUtil::string_to_uint);
+}
+
+void test_to_ull(char const* str, unsigned long long wanted, bool error)
+{
+ test_to_number(str, wanted, error, QUtil::string_to_ull);
+}
+
void string_conversion_test()
{
std::cout << QUtil::int_to_string(16059) << std::endl
@@ -105,6 +117,8 @@ void string_conversion_test()
long long int_min_minus_1 = static_cast<long long>(INT_MIN) - 1;
std::string int_max_plus_1_str = QUtil::int_to_string(int_max_plus_1);
std::string int_min_minus_1_str = QUtil::int_to_string(int_min_minus_1);
+ std::string small_positive = QUtil::uint_to_string(16059U);
+ std::string small_negative = QUtil::int_to_string(-16059);
test_to_int(int_min_str.c_str(), INT_MIN, false);
test_to_int(int_max_str.c_str(), INT_MAX, false);
test_to_int(int_max_plus_1_str.c_str(), 0, true);
@@ -113,6 +127,11 @@ void string_conversion_test()
test_to_ll(int_max_plus_1_str.c_str(), int_max_plus_1, false);
test_to_ll(int_min_minus_1_str.c_str(), int_min_minus_1, false);
test_to_ll("99999999999999999999999999999999999999999999999999", 0, true);
+ test_to_uint(small_positive.c_str(), 16059U, false);
+ test_to_uint(small_negative.c_str(), 0, true);
+ test_to_uint("9999999999", 0, true);
+ test_to_ull(small_positive.c_str(), 16059U, false);
+ test_to_ull(small_negative.c_str(), 0, true);
}
void os_wrapper_test()
@@ -159,7 +178,7 @@ void getenv_test()
static void print_utf8(unsigned long val)
{
std::string result = QUtil::toUTF8(val);
- std::cout << "0x" << QUtil::int_to_string_base(val, 16) << " ->";
+ std::cout << "0x" << QUtil::uint_to_string_base(val, 16) << " ->";
if (val < 0xfffe)
{
std::cout << " " << result;
@@ -199,7 +218,7 @@ void to_utf8_test()
static void print_utf16(unsigned long val)
{
std::string result = QUtil::toUTF16(val);
- std::cout << "0x" << QUtil::int_to_string_base(val, 16) << " ->";
+ std::cout << "0x" << QUtil::uint_to_string_base(val, 16) << " ->";
for (std::string::iterator iter = result.begin();
iter != result.end(); ++iter)
{
@@ -249,7 +268,7 @@ void transcoding_test(std::string (*to_utf8)(std::string const&),
std::string back;
for (int i = 128; i <= last; ++i)
{
- in.at(0) = static_cast<unsigned char>(i);
+ in.at(0) = static_cast<char>(static_cast<unsigned char>(i));
out = (*to_utf8)(in);
std::string wanted = (out == "\xef\xbf\xbd") ? unknown : in;
back = (*from_utf8)(out, '?');